From e7df2998a129c09239c4e6bb6812c0a8ded1d3ff Mon Sep 17 00:00:00 2001 From: Abel Milash Date: Mon, 18 May 2026 10:10:34 -0700 Subject: [PATCH 01/34] Rebase refactoring on main (post GA API merge) - Reset to main (which is now PR #175) - Re-apply: _ODataBase, _BatchBase, _QueryBuilderBase, _BatchContext Protocol, _operation_context in base, Self type annotation - Re-export multipart helpers from _batch.py for test compatibility - Update test_sql_parse.py patch target to _odata_base.urlparse Co-Authored-By: Claude Sonnet 4.6 --- src/PowerPlatform/Dataverse/data/_batch.py | 513 +--------- .../Dataverse/data/_batch_base.py | 513 ++++++++++ src/PowerPlatform/Dataverse/data/_odata.py | 870 +--------------- .../Dataverse/data/_odata_base.py | 936 ++++++++++++++++++ .../Dataverse/models/query_builder.py | 86 +- .../Dataverse/operations/batch.py | 33 +- tests/unit/data/test_sql_parse.py | 2 +- 7 files changed, 1581 insertions(+), 1372 deletions(-) create mode 100644 src/PowerPlatform/Dataverse/data/_batch_base.py create mode 100644 src/PowerPlatform/Dataverse/data/_odata_base.py diff --git a/src/PowerPlatform/Dataverse/data/_batch.py b/src/PowerPlatform/Dataverse/data/_batch.py index b848c5a0..8e2e85d9 100644 --- a/src/PowerPlatform/Dataverse/data/_batch.py +++ b/src/PowerPlatform/Dataverse/data/_batch.py @@ -5,239 +5,56 @@ from __future__ import annotations -import json -import re import uuid -from dataclasses import dataclass, field -from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union - -from ..core.errors import HttpError, MetadataError, ValidationError -from ..core._error_codes import METADATA_TABLE_NOT_FOUND, METADATA_COLUMN_NOT_FOUND, _http_subcode -from ..models.batch import BatchItemResponse, BatchResult -from ..models.relationship import ( - LookupAttributeMetadata, - OneToManyRelationshipMetadata, - ManyToManyRelationshipMetadata, -) -from ..models.upsert import UpsertItem -from ..common.constants import CASCADE_BEHAVIOR_REMOVE_LINK +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union + +from ..core.errors import MetadataError, ValidationError +from ..core._error_codes import METADATA_TABLE_NOT_FOUND, METADATA_COLUMN_NOT_FOUND +from ..models.batch import BatchResult from ._raw_request import _RawRequest -from ._odata import _GUID_RE +from ._batch_base import ( + _BatchBase, + _RecordCreate, + _RecordUpdate, + _RecordDelete, + _RecordGet, + _RecordList, + _RecordUpsert, + _TableCreate, + _TableDelete, + _TableGet, + _TableList, + _TableAddColumns, + _TableRemoveColumns, + _TableCreateOneToMany, + _TableCreateManyToMany, + _TableDeleteRelationship, + _TableGetRelationship, + _TableCreateLookupField, + _QuerySql, + _ChangeSet, + _ChangeSetBatchItem, + _MAX_BATCH_SIZE, + _CRLF, + _extract_boundary, + _raise_top_level_batch_error, + _split_multipart, + _parse_mime_part, + _parse_http_response_part, +) if TYPE_CHECKING: from ._odata import _ODataClient __all__ = [] -_CRLF = "\r\n" -_MAX_BATCH_SIZE = 1000 - - -# --------------------------------------------------------------------------- -# Intent dataclasses — one per supported operation type -# (stored at batch-build time; resolved to _RawRequest at execute() time) -# --------------------------------------------------------------------------- - -# --- Record intent types --- - - -@dataclass -class _RecordCreate: - table: str - data: Union[Dict[str, Any], List[Dict[str, Any]]] - content_id: Optional[int] = None # set only for changeset items - - -@dataclass -class _RecordUpdate: - table: str - ids: Union[str, List[str]] - changes: Union[Dict[str, Any], List[Dict[str, Any]]] - content_id: Optional[int] = None # set only for changeset single-record updates - - -@dataclass -class _RecordDelete: - table: str - ids: Union[str, List[str]] - use_bulk_delete: bool = True - content_id: Optional[int] = None # set only for changeset single-record deletes - - -@dataclass -class _RecordGet: - table: str - record_id: str - select: Optional[List[str]] = None - expand: Optional[List[str]] = None - include_annotations: Optional[str] = None - - -@dataclass -class _RecordList: - table: str - select: Optional[List[str]] = None - filter: Optional[str] = None - orderby: Optional[List[str]] = None - top: Optional[int] = None - expand: Optional[List[str]] = None - page_size: Optional[int] = None - count: bool = False - include_annotations: Optional[str] = None - - -@dataclass -class _RecordUpsert: - table: str - items: List[UpsertItem] # always non-empty; normalised by BatchRecordOperations - - -# --- Table intent types --- - - -@dataclass -class _TableCreate: - table: str - columns: Dict[str, Any] - solution: Optional[str] = None - primary_column: Optional[str] = None - display_name: Optional[str] = None - - -@dataclass -class _TableDelete: - table: str - - -@dataclass -class _TableGet: - table: str - - -@dataclass -class _TableList: - filter: Optional[str] = None - select: Optional[List[str]] = None - - -@dataclass -class _TableAddColumns: - table: str - columns: Dict[str, Any] - - -@dataclass -class _TableRemoveColumns: - table: str - columns: Union[str, List[str]] - - -@dataclass -class _TableCreateOneToMany: - lookup: LookupAttributeMetadata - relationship: OneToManyRelationshipMetadata - solution: Optional[str] = None - - -@dataclass -class _TableCreateManyToMany: - relationship: ManyToManyRelationshipMetadata - solution: Optional[str] = None - - -@dataclass -class _TableDeleteRelationship: - relationship_id: str - - -@dataclass -class _TableGetRelationship: - schema_name: str - - -@dataclass -class _TableCreateLookupField: - referencing_table: str - lookup_field_name: str - referenced_table: str - display_name: Optional[str] = None - description: Optional[str] = None - required: bool = False - cascade_delete: str = CASCADE_BEHAVIOR_REMOVE_LINK - solution: Optional[str] = None - language_code: int = 1033 - - -# --- Query intent types --- - - -@dataclass -class _QuerySql: - sql: str - - -# --------------------------------------------------------------------------- -# Changeset container -# --------------------------------------------------------------------------- - - -@dataclass -class _ChangeSet: - """Ordered group of single-record write operations that execute atomically. - - Content-IDs are allocated from ``_counter``, a single-element ``List[int]`` - that is shared across all changesets in the same batch. Passing the same - list object to every ``_ChangeSet`` created by a :class:`BatchRequest` - ensures Content-ID values are unique within the entire batch request, not - just within an individual changeset, as required by the OData spec. - - When constructed in isolation (e.g. in unit tests), ``_counter`` defaults - to a fresh ``[1]`` so the class remains self-contained. - """ - - operations: List[Union[_RecordCreate, _RecordUpdate, _RecordDelete]] = field(default_factory=list) - _counter: List[int] = field(default_factory=lambda: [1], repr=False) - - def add_create(self, table: str, data: Dict[str, Any]) -> str: - """Add a single-record create; return its content-ID reference string.""" - cid = self._counter[0] - self._counter[0] += 1 - self.operations.append(_RecordCreate(table=table, data=data, content_id=cid)) - return f"${cid}" - - def add_update(self, table: str, record_id: str, changes: Dict[str, Any]) -> None: - """Add a single-record update (record_id may be a '$n' reference).""" - cid = self._counter[0] - self._counter[0] += 1 - self.operations.append(_RecordUpdate(table=table, ids=record_id, changes=changes, content_id=cid)) - - def add_delete(self, table: str, record_id: str) -> None: - """Add a single-record delete (record_id may be a '$n' reference).""" - cid = self._counter[0] - self._counter[0] += 1 - self.operations.append(_RecordDelete(table=table, ids=record_id, content_id=cid)) - - -# --------------------------------------------------------------------------- -# Changeset batch item -# (_RawRequest is imported from ._raw_request — defined there so _odata.py -# can also import it without a circular dependency) -# --------------------------------------------------------------------------- - - -@dataclass -class _ChangeSetBatchItem: - """A resolved changeset — serialised as a nested multipart/mixed part.""" - - requests: List[_RawRequest] - # --------------------------------------------------------------------------- # Batch client: resolves intents → raw requests → multipart body → HTTP → result # --------------------------------------------------------------------------- -class _BatchClient: +class _BatchClient(_BatchBase): """ Serialises a list of intent objects into an OData ``$batch`` multipart/mixed request, dispatches it, and parses the response. @@ -245,9 +62,6 @@ class _BatchClient: :param od: The active OData client (provides helpers and HTTP transport). """ - def __init__(self, od: "_ODataClient") -> None: - self._od = od - # ------------------------------------------------------------------ # Public entry point # ------------------------------------------------------------------ @@ -449,19 +263,10 @@ def _require_entity_metadata(self, table: str) -> str: ) return ent["MetadataId"] - def _resolve_table_create(self, op: _TableCreate) -> List[_RawRequest]: - return [self._od._build_create_entity(op.table, op.columns, op.solution, op.primary_column, op.display_name)] - def _resolve_table_delete(self, op: _TableDelete) -> List[_RawRequest]: metadata_id = self._require_entity_metadata(op.table) return [self._od._build_delete_entity(metadata_id)] - def _resolve_table_get(self, op: _TableGet) -> List[_RawRequest]: - return [self._od._build_get_entity(op.table)] - - def _resolve_table_list(self, op: _TableList) -> List[_RawRequest]: - return [self._od._build_list_entities(filter=op.filter, select=op.select)] - def _resolve_table_add_columns(self, op: _TableAddColumns) -> List[_RawRequest]: metadata_id = self._require_entity_metadata(op.table) return [self._od._build_create_column(metadata_id, col_name, dtype) for col_name, dtype in op.columns.items()] @@ -482,255 +287,9 @@ def _resolve_table_remove_columns(self, op: _TableRemoveColumns) -> List[_RawReq requests.append(self._od._build_delete_column(metadata_id, attr_meta["MetadataId"])) return requests - def _resolve_table_create_one_to_many(self, op: _TableCreateOneToMany) -> List[_RawRequest]: - body = op.relationship.to_dict() - body["Lookup"] = op.lookup.to_dict() - return [self._od._build_create_relationship(body, solution=op.solution)] - - def _resolve_table_create_many_to_many(self, op: _TableCreateManyToMany) -> List[_RawRequest]: - return [self._od._build_create_relationship(op.relationship.to_dict(), solution=op.solution)] - - def _resolve_table_delete_relationship(self, op: _TableDeleteRelationship) -> List[_RawRequest]: - return [self._od._build_delete_relationship(op.relationship_id)] - - def _resolve_table_get_relationship(self, op: _TableGetRelationship) -> List[_RawRequest]: - return [self._od._build_get_relationship(op.schema_name)] - - def _resolve_table_create_lookup_field(self, op: _TableCreateLookupField) -> List[_RawRequest]: - lookup, relationship = self._od._build_lookup_field_models( - referencing_table=op.referencing_table, - lookup_field_name=op.lookup_field_name, - referenced_table=op.referenced_table, - display_name=op.display_name, - description=op.description, - required=op.required, - cascade_delete=op.cascade_delete, - language_code=op.language_code, - ) - body = relationship.to_dict() - body["Lookup"] = lookup.to_dict() - return [self._od._build_create_relationship(body, solution=op.solution)] - # ------------------------------------------------------------------ # Query resolvers — delegate to _ODataClient._build_* methods # ------------------------------------------------------------------ def _resolve_query_sql(self, op: _QuerySql) -> List[_RawRequest]: return [self._od._build_sql(op.sql)] - - # ------------------------------------------------------------------ - # Multipart serialisation - # ------------------------------------------------------------------ - - def _build_batch_body( - self, - resolved: List[Union[_RawRequest, _ChangeSetBatchItem]], - batch_boundary: str, - ) -> str: - parts: List[str] = [] - for item in resolved: - if isinstance(item, _ChangeSetBatchItem): - parts.append(self._serialize_changeset_item(item, batch_boundary)) - else: - parts.append(self._serialize_raw_request(item, batch_boundary)) - return "".join(parts) + f"--{batch_boundary}--{_CRLF}" - - def _serialize_raw_request(self, req: _RawRequest, boundary: str) -> str: - """Serialise a single operation as a multipart/mixed part with CRLF line endings.""" - part_header_lines = [ - f"--{boundary}", - "Content-Type: application/http", - "Content-Transfer-Encoding: binary", - ] - if req.content_id is not None: - part_header_lines.append(f"Content-ID: {req.content_id}") - - inner_lines = [f"{req.method} {req.url} HTTP/1.1"] - if req.body is not None: - inner_lines.append("Content-Type: application/json; type=entry") - if req.headers: - for k, v in req.headers.items(): - inner_lines.append(f"{k}: {v}") - inner_lines.append("") # blank line — end of inner headers - if req.body is not None: - inner_lines.append(req.body) - - part_header_str = _CRLF.join(part_header_lines) + _CRLF - inner_str = _CRLF.join(inner_lines) - return part_header_str + _CRLF + inner_str + _CRLF - - def _serialize_changeset_item(self, cs: _ChangeSetBatchItem, batch_boundary: str) -> str: - cs_boundary = f"changeset_{uuid.uuid4()}" - cs_parts = [self._serialize_raw_request(r, cs_boundary) for r in cs.requests] - cs_parts.append(f"--{cs_boundary}--{_CRLF}") - cs_body = "".join(cs_parts) - - outer = ( - f"--{batch_boundary}{_CRLF}" f'Content-Type: multipart/mixed; boundary="{cs_boundary}"{_CRLF}' f"{_CRLF}" - ) - return outer + cs_body + _CRLF - - # ------------------------------------------------------------------ - # Response parsing (multipart/mixed) - # ------------------------------------------------------------------ - - def _parse_batch_response(self, response: Any) -> BatchResult: - content_type = response.headers.get("Content-Type", "") - boundary = _extract_boundary(content_type) - if not boundary: - # Non-multipart response: the batch request itself was rejected by Dataverse - # (common for top-level 4xx, e.g. malformed body, missing OData headers). - # Returning an empty BatchResult() here would silently hide the error and - # make has_errors=False, which is actively misleading. Raise instead. - _raise_top_level_batch_error(response) - return BatchResult() # unreachable; satisfies type checkers - parts = _split_multipart(response.text or "", boundary) - responses: List[BatchItemResponse] = [] - for part_headers, part_body in parts: - part_ct = part_headers.get("content-type", "") - if "multipart/mixed" in part_ct: - inner_boundary = _extract_boundary(part_ct) - if inner_boundary: - for ih, ib in _split_multipart(part_body, inner_boundary): - item = _parse_http_response_part(ib, ih.get("content-id")) - if item is not None: - responses.append(item) - else: - item = _parse_http_response_part(part_body, content_id=part_headers.get("content-id")) - if item is not None: - responses.append(item) - return BatchResult(responses=responses) - - -# --------------------------------------------------------------------------- -# Multipart parsing helpers -# --------------------------------------------------------------------------- - - -def _raise_top_level_batch_error(response: Any) -> None: - """Parse a non-multipart batch response and raise HttpError with the service message. - - Dataverse returns ``application/json`` with an ``{"error": {...}}`` payload when - it rejects the batch request at the HTTP level (e.g. malformed multipart body, - missing OData headers). This helper surfaces that detail instead of silently - returning an empty ``BatchResult``. - """ - status_code: int = getattr(response, "status_code", 0) - service_error_code: Optional[str] = None - try: - payload = response.json() - error = payload.get("error", {}) - service_error_code = error.get("code") or None - message: str = error.get("message") or response.text or "Unexpected non-multipart response from $batch" - except Exception: - message = (getattr(response, "text", None) or "") or "Unexpected non-multipart response from $batch" - raise HttpError( - message=f"Batch request rejected by Dataverse: {message}", - status_code=status_code, - subcode=_http_subcode(status_code) if status_code else None, - service_error_code=service_error_code, - ) - - -_BOUNDARY_RE = re.compile(r'boundary="?([^";,\s]+)"?', re.IGNORECASE) - - -def _extract_boundary(content_type: str) -> Optional[str]: - m = _BOUNDARY_RE.search(content_type) - return m.group(1) if m else None - - -def _split_multipart(body: str, boundary: str) -> List[Tuple[Dict[str, str], str]]: - delimiter = f"--{boundary}" - parts: List[Tuple[Dict[str, str], str]] = [] - lines = body.replace("\r\n", "\n").split("\n") - current: List[str] = [] - in_part = False - for line in lines: - stripped = line.rstrip("\r") - if stripped == delimiter: - if in_part and current: - parts.append(_parse_mime_part("\n".join(current))) - current = [] - in_part = True - elif stripped == f"{delimiter}--": - if in_part and current: - parts.append(_parse_mime_part("\n".join(current))) - break - elif in_part: - current.append(line) - return parts - - -def _parse_mime_part(raw: str) -> Tuple[Dict[str, str], str]: - if "\n\n" in raw: - header_block, body = raw.split("\n\n", 1) - else: - header_block, body = raw, "" - headers: Dict[str, str] = {} - for line in header_block.splitlines(): - if ":" in line: - name, _, value = line.partition(":") - headers[name.strip().lower()] = value.strip() - return headers, body.strip() - - -def _parse_http_response_part(text: str, content_id: Optional[str]) -> Optional[BatchItemResponse]: - lines = text.replace("\r\n", "\n").splitlines() - if not lines: - return None - status_line = "" - idx = 0 - for i, line in enumerate(lines): - if line.startswith("HTTP/"): - status_line = line - idx = i + 1 - break - if not status_line: - return None - parts = status_line.split(" ", 2) - if len(parts) < 2: - return None - try: - status_code = int(parts[1]) - except ValueError: - return None - resp_headers: Dict[str, str] = {} - body_start = idx - for i in range(idx, len(lines)): - if lines[i] == "": - body_start = i + 1 - break - if ":" in lines[i]: - name, _, value = lines[i].partition(":") - resp_headers[name.strip().lower()] = value.strip() - entity_id: Optional[str] = None - odata_id = resp_headers.get("odata-entityid", "") - if odata_id: - m = _GUID_RE.search(odata_id) - if m: - entity_id = m.group(0) - body_text = "\n".join(lines[body_start:]).strip() - data: Optional[Dict[str, Any]] = None - error_message: Optional[str] = None - error_code: Optional[str] = None - if body_text: - try: - parsed = json.loads(body_text) - if isinstance(parsed, dict): - err = parsed.get("error") - if isinstance(err, dict): - error_message = err.get("message") - error_code = err.get("code") - else: - data = parsed - except (json.JSONDecodeError, ValueError): - pass - return BatchItemResponse( - status_code=status_code, - content_id=content_id, - entity_id=entity_id, - data=data, - error_message=error_message, - error_code=error_code, - ) diff --git a/src/PowerPlatform/Dataverse/data/_batch_base.py b/src/PowerPlatform/Dataverse/data/_batch_base.py new file mode 100644 index 00000000..46b247de --- /dev/null +++ b/src/PowerPlatform/Dataverse/data/_batch_base.py @@ -0,0 +1,513 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +"""Shared intent types, multipart helpers, and pure-logic base for the Dataverse batch client. + +Contains no I/O. Subclasses add the HTTP transport layer (sync or async). +""" + +from __future__ import annotations + +import json +import re +import uuid +from dataclasses import dataclass, field +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union + +from ..core.errors import HttpError, ValidationError +from ..core._error_codes import _http_subcode +from ..models.batch import BatchItemResponse, BatchResult +from ..models.relationship import ( + LookupAttributeMetadata, + OneToManyRelationshipMetadata, + ManyToManyRelationshipMetadata, +) +from ..models.upsert import UpsertItem +from ..common.constants import CASCADE_BEHAVIOR_REMOVE_LINK +from ._raw_request import _RawRequest +from ._odata_base import _GUID_RE + +if TYPE_CHECKING: + from ._odata_base import _ODataBase + +__all__ = [] + +_CRLF = "\r\n" +_MAX_BATCH_SIZE = 1000 + + +# --------------------------------------------------------------------------- +# Intent dataclasses — one per supported operation type +# (stored at batch-build time; resolved to _RawRequest at execute() time) +# --------------------------------------------------------------------------- + +# --- Record intent types --- + + +@dataclass +class _RecordCreate: + table: str + data: Union[Dict[str, Any], List[Dict[str, Any]]] + content_id: Optional[int] = None # set only for changeset items + + +@dataclass +class _RecordUpdate: + table: str + ids: Union[str, List[str]] + changes: Union[Dict[str, Any], List[Dict[str, Any]]] + content_id: Optional[int] = None # set only for changeset single-record updates + + +@dataclass +class _RecordDelete: + table: str + ids: Union[str, List[str]] + use_bulk_delete: bool = True + content_id: Optional[int] = None # set only for changeset single-record deletes + + +@dataclass +class _RecordGet: + table: str + record_id: str + select: Optional[List[str]] = None + expand: Optional[List[str]] = None + include_annotations: Optional[str] = None + + +@dataclass +class _RecordList: + table: str + select: Optional[List[str]] = None + filter: Optional[str] = None + orderby: Optional[List[str]] = None + top: Optional[int] = None + expand: Optional[List[str]] = None + page_size: Optional[int] = None + count: bool = False + include_annotations: Optional[str] = None + + +@dataclass +class _RecordUpsert: + table: str + items: List[UpsertItem] # always non-empty; normalised by BatchRecordOperations + + +# --- Table intent types --- + + +@dataclass +class _TableCreate: + table: str + columns: Dict[str, Any] + solution: Optional[str] = None + primary_column: Optional[str] = None + display_name: Optional[str] = None + + +@dataclass +class _TableDelete: + table: str + + +@dataclass +class _TableGet: + table: str + + +@dataclass +class _TableList: + filter: Optional[str] = None + select: Optional[List[str]] = None + + +@dataclass +class _TableAddColumns: + table: str + columns: Dict[str, Any] + + +@dataclass +class _TableRemoveColumns: + table: str + columns: Union[str, List[str]] + + +@dataclass +class _TableCreateOneToMany: + lookup: LookupAttributeMetadata + relationship: OneToManyRelationshipMetadata + solution: Optional[str] = None + + +@dataclass +class _TableCreateManyToMany: + relationship: ManyToManyRelationshipMetadata + solution: Optional[str] = None + + +@dataclass +class _TableDeleteRelationship: + relationship_id: str + + +@dataclass +class _TableGetRelationship: + schema_name: str + + +@dataclass +class _TableCreateLookupField: + referencing_table: str + lookup_field_name: str + referenced_table: str + display_name: Optional[str] = None + description: Optional[str] = None + required: bool = False + cascade_delete: str = CASCADE_BEHAVIOR_REMOVE_LINK + solution: Optional[str] = None + language_code: int = 1033 + + +# --- Query intent types --- + + +@dataclass +class _QuerySql: + sql: str + + +# --------------------------------------------------------------------------- +# Changeset container +# --------------------------------------------------------------------------- + + +@dataclass +class _ChangeSet: + """Ordered group of single-record write operations that execute atomically. + + Content-IDs are allocated from ``_counter``, a single-element ``List[int]`` + that is shared across all changesets in the same batch. Passing the same + list object to every ``_ChangeSet`` created by a :class:`BatchRequest` + ensures Content-ID values are unique within the entire batch request, not + just within an individual changeset, as required by the OData spec. + + When constructed in isolation (e.g. in unit tests), ``_counter`` defaults + to a fresh ``[1]`` so the class remains self-contained. + """ + + operations: List[Union[_RecordCreate, _RecordUpdate, _RecordDelete]] = field(default_factory=list) + _counter: List[int] = field(default_factory=lambda: [1], repr=False) + + def add_create(self, table: str, data: Dict[str, Any]) -> str: + """Add a single-record create; return its content-ID reference string.""" + cid = self._counter[0] + self._counter[0] += 1 + self.operations.append(_RecordCreate(table=table, data=data, content_id=cid)) + return f"${cid}" + + def add_update(self, table: str, record_id: str, changes: Dict[str, Any]) -> None: + """Add a single-record update (record_id may be a '$n' reference).""" + cid = self._counter[0] + self._counter[0] += 1 + self.operations.append(_RecordUpdate(table=table, ids=record_id, changes=changes, content_id=cid)) + + def add_delete(self, table: str, record_id: str) -> None: + """Add a single-record delete (record_id may be a '$n' reference).""" + cid = self._counter[0] + self._counter[0] += 1 + self.operations.append(_RecordDelete(table=table, ids=record_id, content_id=cid)) + + +# --------------------------------------------------------------------------- +# Changeset batch item +# (_RawRequest is imported from ._raw_request — defined there so _odata.py +# can also import it without a circular dependency) +# --------------------------------------------------------------------------- + + +@dataclass +class _ChangeSetBatchItem: + """A resolved changeset — serialised as a nested multipart/mixed part.""" + + requests: List[_RawRequest] + + +# --------------------------------------------------------------------------- +# Batch base: pure serialisation and pure table resolvers +# --------------------------------------------------------------------------- + + +class _BatchBase: + """Pure-logic base for the Dataverse batch client. + + Provides multipart serialisation, response parsing, and the subset of + intent resolvers that require no I/O. Subclasses must supply ``execute`` + and the I/O-dependent resolvers. + + :param od: The active OData client (provides helpers and HTTP transport). + """ + + def __init__(self, od: "_ODataBase") -> None: + self._od = od + + # ------------------------------------------------------------------ + # Pure table resolvers — delegate to _ODataBase._build_* methods + # ------------------------------------------------------------------ + + def _resolve_table_create(self, op: _TableCreate) -> List[_RawRequest]: + return [self._od._build_create_entity(op.table, op.columns, op.solution, op.primary_column, op.display_name)] + + def _resolve_table_get(self, op: _TableGet) -> List[_RawRequest]: + return [self._od._build_get_entity(op.table)] + + def _resolve_table_list(self, op: _TableList) -> List[_RawRequest]: + return [self._od._build_list_entities(filter=op.filter, select=op.select)] + + def _resolve_table_create_one_to_many(self, op: _TableCreateOneToMany) -> List[_RawRequest]: + body = op.relationship.to_dict() + body["Lookup"] = op.lookup.to_dict() + return [self._od._build_create_relationship(body, solution=op.solution)] + + def _resolve_table_create_many_to_many(self, op: _TableCreateManyToMany) -> List[_RawRequest]: + return [self._od._build_create_relationship(op.relationship.to_dict(), solution=op.solution)] + + def _resolve_table_delete_relationship(self, op: _TableDeleteRelationship) -> List[_RawRequest]: + return [self._od._build_delete_relationship(op.relationship_id)] + + def _resolve_table_get_relationship(self, op: _TableGetRelationship) -> List[_RawRequest]: + return [self._od._build_get_relationship(op.schema_name)] + + def _resolve_table_create_lookup_field(self, op: _TableCreateLookupField) -> List[_RawRequest]: + lookup, relationship = self._od._build_lookup_field_models( + referencing_table=op.referencing_table, + lookup_field_name=op.lookup_field_name, + referenced_table=op.referenced_table, + display_name=op.display_name, + description=op.description, + required=op.required, + cascade_delete=op.cascade_delete, + language_code=op.language_code, + ) + body = relationship.to_dict() + body["Lookup"] = lookup.to_dict() + return [self._od._build_create_relationship(body, solution=op.solution)] + + # ------------------------------------------------------------------ + # Multipart serialisation + # ------------------------------------------------------------------ + + def _build_batch_body( + self, + resolved: List[Union[_RawRequest, _ChangeSetBatchItem]], + batch_boundary: str, + ) -> str: + parts: List[str] = [] + for item in resolved: + if isinstance(item, _ChangeSetBatchItem): + parts.append(self._serialize_changeset_item(item, batch_boundary)) + else: + parts.append(self._serialize_raw_request(item, batch_boundary)) + return "".join(parts) + f"--{batch_boundary}--{_CRLF}" + + def _serialize_raw_request(self, req: _RawRequest, boundary: str) -> str: + """Serialise a single operation as a multipart/mixed part with CRLF line endings.""" + part_header_lines = [ + f"--{boundary}", + "Content-Type: application/http", + "Content-Transfer-Encoding: binary", + ] + if req.content_id is not None: + part_header_lines.append(f"Content-ID: {req.content_id}") + + inner_lines = [f"{req.method} {req.url} HTTP/1.1"] + if req.body is not None: + inner_lines.append("Content-Type: application/json; type=entry") + if req.headers: + for k, v in req.headers.items(): + inner_lines.append(f"{k}: {v}") + inner_lines.append("") # blank line — end of inner headers + if req.body is not None: + inner_lines.append(req.body) + + part_header_str = _CRLF.join(part_header_lines) + _CRLF + inner_str = _CRLF.join(inner_lines) + return part_header_str + _CRLF + inner_str + _CRLF + + def _serialize_changeset_item(self, cs: _ChangeSetBatchItem, batch_boundary: str) -> str: + cs_boundary = f"changeset_{uuid.uuid4()}" + cs_parts = [self._serialize_raw_request(r, cs_boundary) for r in cs.requests] + cs_parts.append(f"--{cs_boundary}--{_CRLF}") + cs_body = "".join(cs_parts) + + outer = ( + f"--{batch_boundary}{_CRLF}" f'Content-Type: multipart/mixed; boundary="{cs_boundary}"{_CRLF}' f"{_CRLF}" + ) + return outer + cs_body + _CRLF + + # ------------------------------------------------------------------ + # Response parsing (multipart/mixed) + # ------------------------------------------------------------------ + + def _parse_batch_response(self, response: Any) -> BatchResult: + content_type = response.headers.get("Content-Type", "") + boundary = _extract_boundary(content_type) + if not boundary: + # Non-multipart response: the batch request itself was rejected by Dataverse + # (common for top-level 4xx, e.g. malformed body, missing OData headers). + # Returning an empty BatchResult() here would silently hide the error and + # make has_errors=False, which is actively misleading. Raise instead. + _raise_top_level_batch_error(response) + return BatchResult() # unreachable; satisfies type checkers + parts = _split_multipart(response.text or "", boundary) + responses: List[BatchItemResponse] = [] + for part_headers, part_body in parts: + part_ct = part_headers.get("content-type", "") + if "multipart/mixed" in part_ct: + inner_boundary = _extract_boundary(part_ct) + if inner_boundary: + for ih, ib in _split_multipart(part_body, inner_boundary): + item = _parse_http_response_part(ib, ih.get("content-id")) + if item is not None: + responses.append(item) + else: + item = _parse_http_response_part(part_body, content_id=part_headers.get("content-id")) + if item is not None: + responses.append(item) + return BatchResult(responses=responses) + + +# --------------------------------------------------------------------------- +# Multipart parsing helpers +# --------------------------------------------------------------------------- + + +def _raise_top_level_batch_error(response: Any) -> None: + """Parse a non-multipart batch response and raise HttpError with the service message. + + Dataverse returns ``application/json`` with an ``{"error": {...}}`` payload when + it rejects the batch request at the HTTP level (e.g. malformed multipart body, + missing OData headers). This helper surfaces that detail instead of silently + returning an empty ``BatchResult``. + """ + status_code: int = getattr(response, "status_code", 0) + service_error_code: Optional[str] = None + try: + payload = response.json() + error = payload.get("error", {}) + service_error_code = error.get("code") or None + message: str = error.get("message") or response.text or "Unexpected non-multipart response from $batch" + except Exception: + message = (getattr(response, "text", None) or "") or "Unexpected non-multipart response from $batch" + raise HttpError( + message=f"Batch request rejected by Dataverse: {message}", + status_code=status_code, + subcode=_http_subcode(status_code) if status_code else None, + service_error_code=service_error_code, + ) + + +_BOUNDARY_RE = re.compile(r'boundary="?([^";,\s]+)"?', re.IGNORECASE) + + +def _extract_boundary(content_type: str) -> Optional[str]: + m = _BOUNDARY_RE.search(content_type) + return m.group(1) if m else None + + +def _split_multipart(body: str, boundary: str) -> List[Tuple[Dict[str, str], str]]: + delimiter = f"--{boundary}" + parts: List[Tuple[Dict[str, str], str]] = [] + lines = body.replace("\r\n", "\n").split("\n") + current: List[str] = [] + in_part = False + for line in lines: + stripped = line.rstrip("\r") + if stripped == delimiter: + if in_part and current: + parts.append(_parse_mime_part("\n".join(current))) + current = [] + in_part = True + elif stripped == f"{delimiter}--": + if in_part and current: + parts.append(_parse_mime_part("\n".join(current))) + break + elif in_part: + current.append(line) + return parts + + +def _parse_mime_part(raw: str) -> Tuple[Dict[str, str], str]: + if "\n\n" in raw: + header_block, body = raw.split("\n\n", 1) + else: + header_block, body = raw, "" + headers: Dict[str, str] = {} + for line in header_block.splitlines(): + if ":" in line: + name, _, value = line.partition(":") + headers[name.strip().lower()] = value.strip() + return headers, body.strip() + + +def _parse_http_response_part(text: str, content_id: Optional[str]) -> Optional[BatchItemResponse]: + lines = text.replace("\r\n", "\n").splitlines() + if not lines: + return None + status_line = "" + idx = 0 + for i, line in enumerate(lines): + if line.startswith("HTTP/"): + status_line = line + idx = i + 1 + break + if not status_line: + return None + parts = status_line.split(" ", 2) + if len(parts) < 2: + return None + try: + status_code = int(parts[1]) + except ValueError: + return None + resp_headers: Dict[str, str] = {} + body_start = idx + for i in range(idx, len(lines)): + if lines[i] == "": + body_start = i + 1 + break + if ":" in lines[i]: + name, _, value = lines[i].partition(":") + resp_headers[name.strip().lower()] = value.strip() + entity_id: Optional[str] = None + odata_id = resp_headers.get("odata-entityid", "") + if odata_id: + m = _GUID_RE.search(odata_id) + if m: + entity_id = m.group(0) + body_text = "\n".join(lines[body_start:]).strip() + data: Optional[Dict[str, Any]] = None + error_message: Optional[str] = None + error_code: Optional[str] = None + if body_text: + try: + parsed = json.loads(body_text) + if isinstance(parsed, dict): + err = parsed.get("error") + if isinstance(err, dict): + error_message = err.get("message") + error_code = err.get("code") + else: + data = parsed + except (json.JSONDecodeError, ValueError): + pass + return BatchItemResponse( + status_code=status_code, + content_id=content_id, + entity_id=entity_id, + data=data, + error_message=error_message, + error_code=error_code, + ) diff --git a/src/PowerPlatform/Dataverse/data/_odata.py b/src/PowerPlatform/Dataverse/data/_odata.py index 8b27dd20..2264ccf9 100644 --- a/src/PowerPlatform/Dataverse/data/_odata.py +++ b/src/PowerPlatform/Dataverse/data/_odata.py @@ -12,27 +12,15 @@ from dataclasses import dataclass, field import unicodedata import time -import re import json -import uuid import warnings from datetime import datetime, timezone -import importlib.resources as ir -from contextlib import contextmanager -from contextvars import ContextVar -from urllib.parse import quote as _url_quote, parse_qs, urlparse +from urllib.parse import quote as _url_quote from ..core._http import _HttpClient from ._upload import _FileUploadMixin from ._relationships import _RelationshipOperationsMixin -from ..models.relationship import ( - LookupAttributeMetadata, - OneToManyRelationshipMetadata, - CascadeConfiguration, -) -from ..models.labels import Label, LocalizedLabel -from ..common.constants import CASCADE_BEHAVIOR_REMOVE_LINK from ..core.errors import * from ._raw_request import _RawRequest from ..core._error_codes import ( @@ -48,120 +36,21 @@ METADATA_TABLE_NOT_FOUND, METADATA_TABLE_ALREADY_EXISTS, METADATA_COLUMN_NOT_FOUND, - VALIDATION_UNSUPPORTED_CACHE_KIND, ) -from .. import __version__ as _SDK_VERSION - -_USER_AGENT = f"DataverseSvcPythonClient:{_SDK_VERSION}" -_GUID_RE = re.compile(r"[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}") -_CALL_SCOPE_CORRELATION_ID: ContextVar[Optional[str]] = ContextVar("_CALL_SCOPE_CORRELATION_ID", default=None) -_DEFAULT_EXPECTED_STATUSES: tuple[int, ...] = (200, 201, 202, 204) - - -def _extract_pagingcookie(next_link: str) -> Optional[str]: - """Extract the raw pagingcookie value from a SQL ``@odata.nextLink`` URL. - - The Dataverse SQL endpoint has a server-side bug where the pagingcookie - (containing first/last record GUIDs) does not advance between pages even - though ``pagenumber`` increments. Detecting a repeated cookie lets the - pagination loop break instead of looping indefinitely. - - Returns the pagingcookie string if present, or ``None`` if not found. - """ - try: - qs = parse_qs(urlparse(next_link).query) - skiptoken = qs.get("$skiptoken", [None])[0] - if not skiptoken: - return None - # parse_qs already URL-decodes the value once, giving the outer XML with - # pagingcookie still percent-encoded (e.g. pagingcookie="%3ccookie..."). - # A second decode is intentionally omitted: decoding again would turn %22 - # into " inside the cookie XML, breaking the regex and causing every page - # to extract the same truncated prefix regardless of the actual GUIDs. - m = re.search(r'pagingcookie="([^"]+)"', skiptoken) - if m: - return m.group(1) - except Exception: - pass - return None - - -@dataclass -class _RequestContext: - """Structured request context used by ``_request`` to clarify payload and metadata.""" - - method: str - url: str - expected: tuple[int, ...] = _DEFAULT_EXPECTED_STATUSES - headers: Optional[Dict[str, str]] = None - kwargs: Dict[str, Any] = field(default_factory=dict) - - @classmethod - def build( - cls, - method: str, - url: str, - *, - expected: tuple[int, ...] = _DEFAULT_EXPECTED_STATUSES, - merge_headers: Optional[Callable[[Optional[Dict[str, str]]], Dict[str, str]]] = None, - **kwargs: Any, - ) -> "_RequestContext": - headers = kwargs.get("headers") - headers = merge_headers(headers) if merge_headers else (headers or {}) - headers.setdefault("x-ms-client-request-id", str(uuid.uuid4())) - headers.setdefault("x-ms-correlation-id", _CALL_SCOPE_CORRELATION_ID.get()) - kwargs["headers"] = headers - return cls( - method=method, - url=url, - expected=expected, - headers=headers, - kwargs=kwargs or {}, - ) +from ._odata_base import ( + _ODataBase, + _GUID_RE, + _extract_pagingcookie, + _USER_AGENT, + _DEFAULT_EXPECTED_STATUSES, + _RequestContext, +) -class _ODataClient(_FileUploadMixin, _RelationshipOperationsMixin): +class _ODataClient(_FileUploadMixin, _RelationshipOperationsMixin, _ODataBase): """Dataverse Web API client: CRUD, SQL-over-API, and table metadata helpers.""" - @staticmethod - def _escape_odata_quotes(value: str) -> str: - """Escape single quotes for OData queries (by doubling them).""" - return value.replace("'", "''") - - @staticmethod - def _normalize_cache_key(table_schema_name: str) -> str: - """Normalize table_schema_name to lowercase for case-insensitive cache keys.""" - return table_schema_name.lower() if isinstance(table_schema_name, str) else "" - - @staticmethod - def _lowercase_keys(record: Dict[str, Any]) -> Dict[str, Any]: - """Convert all 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. - - Keys containing ``@odata.`` (e.g. ``new_CustomerId@odata.bind``) are - preserved as-is because the navigation property portion before ``@`` - must retain its original casing (case-sensitive navigation property name). The OData - parser validates ``@odata.bind`` property names **case-sensitively** - against the entity's declared navigation properties, so lowercasing - these keys causes ``400 - undeclared property`` errors. - """ - if not isinstance(record, dict): - return record - return {k.lower() if isinstance(k, str) and "@odata." not in k else k: v for k, v in record.items()} - - @staticmethod - def _lowercase_list(items: Optional[List[str]]) -> Optional[List[str]]: - """Convert all strings in a list to lowercase for case-insensitive column names. - - Used for $select and $orderby parameters where column names must be lowercase. - """ - if not items: - return items - return [item.lower() if isinstance(item, str) else item for item in items] - def __init__( self, auth, @@ -183,22 +72,8 @@ def __init__( :type session: :class:`requests.Session` | ``None`` :raises ValueError: If ``base_url`` is empty after stripping. """ + super().__init__(base_url, config) self.auth = auth - self.base_url = (base_url or "").rstrip("/") - if not self.base_url: - raise ValueError("base_url is required.") - self.api = f"{self.base_url}/api/data/v9.2" - self.config = ( - config - or __import__( - "PowerPlatform.Dataverse.core.config", fromlist=["DataverseConfig"] - ).DataverseConfig.from_env() - ) - self._http_logger = None - if self.config.log_config is not None: - from ..core._http_logger import _HttpLogger - - self._http_logger = _HttpLogger(self.config.log_config) self._http = _HttpClient( retries=self.config.http_retries, backoff=self.config.http_backoff, @@ -206,23 +81,6 @@ def __init__( session=session, logger=self._http_logger, ) - ctx_obj = self.config.operation_context - self._operation_context = ctx_obj.user_agent_context if ctx_obj else None - self._logical_to_entityset_cache: dict[str, str] = {} - # Cache: normalized table_schema_name (lowercase) -> primary id attribute (e.g. accountid) - self._logical_primaryid_cache: dict[str, str] = {} - self._picklist_label_cache: dict[str, dict] = {} - self._picklist_cache_ttl_seconds = 3600 # 1 hour TTL - - @contextmanager - def _call_scope(self): - """Context manager to generate a new correlation id for each SDK call scope.""" - shared_id = str(uuid.uuid4()) - token = _CALL_SCOPE_CORRELATION_ID.set(shared_id) - try: - yield shared_id - finally: - _CALL_SCOPE_CORRELATION_ID.reset(token) def close(self) -> None: """Close the OData client and release resources. @@ -230,14 +88,9 @@ def close(self) -> None: Clears all internal caches and closes the underlying HTTP client. Safe to call multiple times. """ - self._logical_to_entityset_cache.clear() - self._logical_primaryid_cache.clear() - self._picklist_label_cache.clear() + super().close() if self._http is not None: self._http.close() - if self._http_logger is not None: - self._http_logger.close() - self._http_logger = None def _headers(self) -> Dict[str, str]: """Build standard OData headers with bearer auth.""" @@ -421,36 +274,6 @@ def _create_multiple(self, entity_set: str, table_schema_name: str, records: Lis return out return [] - def _build_alternate_key_str(self, alternate_key: Dict[str, Any]) -> str: - """Build an OData alternate key segment from a mapping of key names to values. - - String values are single-quoted and escaped; all other values are rendered as-is. - - :param alternate_key: Mapping of alternate key attribute names to their values. - Must be a non-empty dict with string keys. - :type alternate_key: ``dict[str, Any]`` - - :return: Comma-separated key=value pairs suitable for use in a URL segment. - :rtype: ``str`` - - :raises ValueError: If ``alternate_key`` is empty. - :raises TypeError: If any key in ``alternate_key`` is not a string. - """ - if not alternate_key: - raise ValueError("alternate_key must be a non-empty dict") - bad_keys = [k for k in alternate_key if not isinstance(k, str)] - if bad_keys: - raise TypeError(f"alternate_key keys must be strings; got: {bad_keys!r}") - parts = [] - for k, v in alternate_key.items(): - k_lower = k.lower() if isinstance(k, str) else k - if isinstance(v, str): - v_escaped = self._escape_odata_quotes(v) - parts.append(f"{k_lower}='{v_escaped}'") - else: - parts.append(f"{k_lower}={v}") - return ",".join(parts) - def _upsert( self, entity_set: str, @@ -622,23 +445,6 @@ def _delete_multiple( job_id = body.get("JobId") return job_id - def _format_key(self, key: str) -> str: - k = key.strip() - if k.startswith("(") and k.endswith(")"): - return k - # Escape single quotes in alternate key values - if "=" in k and "'" in k: - - def esc(match): - # match.group(1) is the key, match.group(2) is the value - return f"{match.group(1)}='{self._escape_odata_quotes(match.group(2))}'" - - k = re.sub(r"(\w+)=\'([^\']*)\'", esc, k) - return f"({k})" - if len(k) == 36 and "-" in k: - return f"({k})" - return f"({k})" - def _update(self, table_schema_name: str, key: str, data: Dict[str, Any]) -> None: """Update an existing record by GUID. @@ -811,165 +617,6 @@ def _do_request(url: str, *, params: Optional[Dict[str, Any]] = None) -> Dict[st yield [x for x in items if isinstance(x, dict)] next_link = data.get("@odata.nextLink") or data.get("odata.nextLink") if isinstance(data, dict) else None - # ----------------------- SQL guardrail patterns -------------------- - _SQL_WRITE_RE = re.compile( - r"^\s*(?:INSERT|UPDATE|DELETE|DROP|TRUNCATE|ALTER|CREATE|EXEC|GRANT|REVOKE|BULK)\b", - re.IGNORECASE, - ) - _SQL_COMMENT_RE = re.compile(r"/\*[^*]*\*+(?:[^/*][^*]*\*+)*/|--[^\n]*", re.DOTALL) - _SQL_LEADING_WILDCARD_RE = re.compile(r"\bLIKE\s+'%[^']", re.IGNORECASE) - _SQL_IMPLICIT_CROSS_JOIN_RE = re.compile( - r"\bFROM\s+[A-Za-z0-9_]+(?:\s+[A-Za-z0-9_]+)?\s*,\s*[A-Za-z0-9_]+", - re.IGNORECASE, - ) - # Server-blocked SQL patterns (save the round-trip by catching early) - _SQL_UNSUPPORTED_JOIN_RE = re.compile( - r"\b(?:CROSS\s+JOIN|RIGHT\s+(?:OUTER\s+)?JOIN|FULL\s+(?:OUTER\s+)?JOIN)\b", - re.IGNORECASE, - ) - _SQL_UNION_RE = re.compile(r"\bUNION\b", re.IGNORECASE) - _SQL_HAVING_RE = re.compile(r"\bHAVING\b", re.IGNORECASE) - _SQL_CTE_RE = re.compile(r"^\s*WITH\b", re.IGNORECASE) - _SQL_SUBQUERY_RE = re.compile( - r"\bIN\s*\(\s*SELECT\b|\bEXISTS\s*\(\s*SELECT\b|\(\s*SELECT\b.*\bFROM\b", - re.IGNORECASE, - ) - # SELECT * is intentionally rejected -- not a technical limitation but a - # deliberate design decision. Wide entities (e.g. account has 307 columns) - # make SELECT * extremely expensive on shared database infrastructure. - # COUNT(*) is NOT matched because COUNT appears before the *. - _SQL_SELECT_STAR_RE = re.compile( - r"\bSELECT\b\s+(?:DISTINCT\s+)?(?:TOP\s+\d+(?:\s+PERCENT)?\s+)?\*\s", - re.IGNORECASE, - ) - - def _sql_guardrails(self, sql: str) -> str: - """Apply safety guardrails to a SQL query before sending to the server. - - Checks split into two categories: - - **Blocked** (``ValidationError`` -- saves a server round-trip): - - 1. Write statements (INSERT/UPDATE/DELETE/DROP/etc.) - 2. CROSS JOIN, RIGHT JOIN, FULL OUTER JOIN (server rejects these) - 3. UNION / UNION ALL (server rejects) - 4. HAVING clause (server rejects) - 5. CTE / WITH clause (server rejects) - 6. Subqueries -- IN (SELECT ...), EXISTS (SELECT ...) (server rejects) - 7. SELECT * -- intentional design decision, not a technical limitation. - Wide entities make wildcard selects extremely expensive on shared - database infrastructure. ``COUNT(*)`` is not affected. - - **Warned** (``UserWarning`` -- query still executes): - - 8. Leading-wildcard LIKE (full table scan) - 9. Implicit cross join FROM a, b (cartesian product) - - All blocked patterns are also blocked by the server, but catching - them here saves the network round-trip and provides clearer error - messages. To bypass a specific check (e.g., if the server adds - support in the future), all checks are in this single method. - - :param sql: The SQL string (already stripped). - :return: The SQL string (unchanged). - :raises ValidationError: If the SQL contains a blocked pattern. - """ - # --- BLOCKED (save server round-trip) --- - - # 1. Block writes (strip SQL comments first to catch comment-prefixed writes) - sql_no_comments = self._SQL_COMMENT_RE.sub(" ", sql).strip() - if self._SQL_WRITE_RE.search(sql_no_comments): - raise ValidationError( - "SQL endpoint is read-only. Use client.records or " - "client.dataframe for write operations " - "(INSERT/UPDATE/DELETE are not supported).", - subcode=VALIDATION_SQL_WRITE_BLOCKED, - ) - - # 2. Block unsupported JOIN types - m = self._SQL_UNSUPPORTED_JOIN_RE.search(sql) - if m: - raise ValidationError( - f"Unsupported JOIN type: '{m.group(0).strip()}'. " - "Only INNER JOIN and LEFT JOIN are supported by the " - "Dataverse SQL endpoint.", - subcode=VALIDATION_SQL_UNSUPPORTED_SYNTAX, - ) - - # 3. Block UNION - if self._SQL_UNION_RE.search(sql): - raise ValidationError( - "UNION is not supported by the Dataverse SQL endpoint. " - "Execute separate queries and combine results in Python " - "(e.g. pd.concat([df1, df2])).", - subcode=VALIDATION_SQL_UNSUPPORTED_SYNTAX, - ) - - # 4. Block HAVING - if self._SQL_HAVING_RE.search(sql): - raise ValidationError( - "HAVING is not supported by the Dataverse SQL endpoint. " - "Use WHERE to filter before GROUP BY instead.", - subcode=VALIDATION_SQL_UNSUPPORTED_SYNTAX, - ) - - # 5. Block CTE / WITH - if self._SQL_CTE_RE.search(sql): - raise ValidationError( - "CTE (WITH ... AS) is not supported by the Dataverse SQL " - "endpoint. Use separate queries and combine in Python.", - subcode=VALIDATION_SQL_UNSUPPORTED_SYNTAX, - ) - - # 6. Block subqueries - if self._SQL_SUBQUERY_RE.search(sql): - raise ValidationError( - "Subqueries are not supported by the Dataverse SQL " - "endpoint. Use separate SQL calls and combine results " - "in Python (e.g. step 1: get IDs, step 2: WHERE IN).", - subcode=VALIDATION_SQL_UNSUPPORTED_SYNTAX, - ) - - # 7. Block SELECT * -- intentional design decision. - # Wide entities (e.g. account has 307 columns) make wildcard selects - # extremely expensive on shared database infrastructure. - # COUNT(*) is NOT matched: _SQL_SELECT_STAR_RE requires * to be the - # first token after SELECT/DISTINCT/TOP N, so COUNT appears before *. - if self._SQL_SELECT_STAR_RE.search(sql): - raise ValidationError( - "SELECT * is not supported. Specify column names explicitly " - "(e.g. SELECT name, revenue FROM account). " - "Use client.query.sql_columns('account') to discover available columns.", - subcode=VALIDATION_SQL_UNSUPPORTED_SYNTAX, - ) - - # --- WARNED (query still executes) --- - - # 8. Warn on leading-wildcard LIKE - if self._SQL_LEADING_WILDCARD_RE.search(sql): - warnings.warn( - "Query contains a leading-wildcard LIKE pattern " - "(e.g. LIKE '%value'). This forces a full table scan " - "and may degrade performance on large tables. " - "Prefer trailing wildcards (LIKE 'value%') when possible.", - UserWarning, - stacklevel=4, - ) - - # 9. Warn on implicit cross joins (server allows but risky) - if self._SQL_IMPLICIT_CROSS_JOIN_RE.search(sql): - warnings.warn( - "Query uses an implicit cross join (FROM table1, table2). " - "This produces a cartesian product that can generate " - "millions of intermediate rows and degrade shared database " - "performance. Use explicit JOIN...ON syntax instead: " - "FROM table1 a JOIN table2 b ON a.column = b.column", - UserWarning, - stacklevel=4, - ) - - return sql - # --------------------------- SQL Custom API ------------------------- def _query_sql(self, sql: str) -> list[dict[str, Any]]: """Execute a read-only SQL SELECT using the Dataverse Web API ``?sql=`` capability. @@ -1087,25 +734,6 @@ def _query_sql(self, sql: str) -> list[dict[str, Any]]: return results - @staticmethod - def _extract_logical_table(sql: str) -> str: - """Extract the logical table name after the first standalone FROM. - - Examples: - SELECT * FROM account - SELECT col1, startfrom FROM new_sampleitem WHERE col1 = 1 - - """ - if not isinstance(sql, str): - raise ValueError("sql must be a string") - # Mask out single-quoted string literals to avoid matching FROM inside them. - masked = re.sub(r"'([^']|'')*'", "'x'", sql) - pattern = r"\bfrom\b\s+([A-Za-z0-9_]+)" # minimal, single-line regex - m = re.search(pattern, masked, flags=re.IGNORECASE) - if not m: - raise ValueError("Unable to determine table logical name from SQL (expected 'FROM ').") - return m.group(1).lower() - # ---------------------- Entity set resolution ----------------------- def _entity_set_from_schema_name(self, table_schema_name: str) -> str: """Resolve entity set name (plural) from a schema name (singular) name using metadata. @@ -1158,23 +786,6 @@ def _entity_set_from_schema_name(self, table_schema_name: str) -> str: return es # ---------------------- Table metadata helpers ---------------------- - def _label(self, text: str) -> Dict[str, Any]: - lang = int(self.config.language_code) - return { - "@odata.type": "Microsoft.Dynamics.CRM.Label", - "LocalizedLabels": [ - { - "@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel", - "Label": text, - "LanguageCode": lang, - } - ], - } - - def _to_pascal(self, name: str) -> str: - parts = re.split(r"[^A-Za-z0-9]+", name) - return "".join(p[:1].upper() + p[1:] for p in parts if p) - def _get_entity_by_table_schema_name( self, table_schema_name: str, @@ -1348,136 +959,6 @@ def _wait_for_attribute_visibility( f"after {total_wait} seconds (exhausted all retries)." ) from last_error - # ---------------------- Enum / Option Set helpers ------------------ - def _build_localizedlabels_payload(self, translations: Dict[int, str]) -> Dict[str, Any]: - """Build a Dataverse Label object from {: } entries. - - Ensures at least one localized label. Does not deduplicate language codes; last wins. - """ - locs: List[Dict[str, Any]] = [] - for lang, text in translations.items(): - if not isinstance(lang, int): - raise ValueError(f"Language code '{lang}' must be int") - if not isinstance(text, str) or not text.strip(): - raise ValueError(f"Label for lang {lang} must be non-empty string") - locs.append( - { - "@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel", - "Label": text, - "LanguageCode": lang, - } - ) - if not locs: - raise ValueError("At least one translation required") - return { - "@odata.type": "Microsoft.Dynamics.CRM.Label", - "LocalizedLabels": locs, - } - - def _enum_optionset_payload( - self, column_schema_name: str, enum_cls: type[Enum], is_primary_name: bool = False - ) -> Dict[str, Any]: - """Create local (IsGlobal=False) PicklistAttributeMetadata from an Enum subclass. - - Supports translation mapping via optional class attribute `__labels__`: - __labels__ = { 1033: { "Active": "Active", "Inactive": "Inactive" }, - 1036: { "Active": "Actif", "Inactive": "Inactif" } } - - Keys inside per-language dict may be either enum member objects or their names. - If a language lacks a label for a member, member.name is used as fallback. - The client's configured language code is always ensured to exist. - """ - all_member_items = list(enum_cls.__members__.items()) - if not all_member_items: - raise ValueError(f"Enum {enum_cls.__name__} has no members") - - # Duplicate detection - value_to_first_name: Dict[int, str] = {} - for name, member in all_member_items: - val = getattr(member, "value", None) - # Defer non-int validation to later loop for consistency - if val in value_to_first_name and value_to_first_name[val] != name: - raise ValueError( - f"Duplicate enum value {val} in {enum_cls.__name__} (names: {value_to_first_name[val]}, {name})" - ) - value_to_first_name[val] = name - - members = list(enum_cls) - # Validate integer values - for m in members: - if not isinstance(m.value, int): - raise ValueError(f"Enum member '{m.name}' has non-int value '{m.value}' (only int values supported)") - - raw_labels = getattr(enum_cls, "__labels__", None) - labels_by_lang: Dict[int, Dict[str, str]] = {} - if raw_labels is not None: - if not isinstance(raw_labels, dict): - raise ValueError("__labels__ must be a dict {lang:int -> {member: label}}") - # Build a helper map for value -> member name to resolve raw int keys - value_to_name = {m.value: m.name for m in members} - for lang, mapping in raw_labels.items(): - if not isinstance(lang, int): - raise ValueError("Language codes in __labels__ must be ints") - if not isinstance(mapping, dict): - raise ValueError(f"__labels__[{lang}] must be a dict of member names to strings") - labels_by_lang.setdefault(lang, {}) - for k, v in mapping.items(): - # Accept enum member object, its name, or raw int value (from class body reference) - if isinstance(k, enum_cls): - member_name = k.name - elif isinstance(k, int): - member_name = value_to_name.get(k) - if member_name is None: - raise ValueError(f"__labels__[{lang}] has int key {k} not matching any enum value") - else: - member_name = str(k) - if not isinstance(v, str) or not v.strip(): - raise ValueError(f"Label for {member_name} lang {lang} must be non-empty string") - labels_by_lang[lang][member_name] = v - - config_lang = int(self.config.language_code) - # Ensure config language appears (fallback to names) - all_langs = set(labels_by_lang.keys()) | {config_lang} - - options: List[Dict[str, Any]] = [] - for m in sorted(members, key=lambda x: x.value): - per_lang: Dict[int, str] = {} - for lang in all_langs: - label_text = labels_by_lang.get(lang, {}).get(m.name, m.name) - per_lang[lang] = label_text - options.append( - { - "@odata.type": "Microsoft.Dynamics.CRM.OptionMetadata", - "Value": m.value, - "Label": self._build_localizedlabels_payload(per_lang), - } - ) - - attr_label = column_schema_name.split("_")[-1] - return { - "@odata.type": "Microsoft.Dynamics.CRM.PicklistAttributeMetadata", - "SchemaName": column_schema_name, - "DisplayName": self._label(attr_label), - "RequiredLevel": {"Value": "None"}, - "IsPrimaryName": bool(is_primary_name), - "OptionSet": { - "@odata.type": "Microsoft.Dynamics.CRM.OptionSetMetadata", - "IsGlobal": False, - "Options": options, - }, - } - - def _normalize_picklist_label(self, label: str) -> str: - """Normalize a label for case / diacritic insensitive comparison.""" - if not isinstance(label, str): - return "" - # Strip accents - norm = unicodedata.normalize("NFD", label) - norm = "".join(c for c in norm if unicodedata.category(c) != "Mn") - # Collapse whitespace, lowercase - norm = re.sub(r"\s+", " ", norm).strip().lower() - return norm - def _request_metadata_with_retry(self, method: str, url: str, **kwargs): """Fetch metadata with retries on transient errors.""" max_attempts = 5 @@ -1591,105 +1072,6 @@ def _convert_labels_to_ints(self, table_schema_name: str, record: Dict[str, Any] resolved_record[k] = val return resolved_record - def _attribute_payload( - self, column_schema_name: str, dtype: Any, *, is_primary_name: bool = False - ) -> Optional[Dict[str, Any]]: - # Enum-based local option set support - if isinstance(dtype, type) and issubclass(dtype, Enum): - return self._enum_optionset_payload(column_schema_name, dtype, is_primary_name=is_primary_name) - if not isinstance(dtype, str): - raise ValueError( - f"Unsupported column spec type for '{column_schema_name}': {type(dtype)} (expected str or Enum subclass)" - ) - dtype_l = dtype.lower().strip() - label = column_schema_name.split("_")[-1] - if dtype_l in ("string", "text"): - return { - "@odata.type": "Microsoft.Dynamics.CRM.StringAttributeMetadata", - "SchemaName": column_schema_name, - "DisplayName": self._label(label), - "RequiredLevel": {"Value": "None"}, - "MaxLength": 200, - "FormatName": {"Value": "Text"}, - "IsPrimaryName": bool(is_primary_name), - } - if dtype_l in ("memo", "multiline"): - return { - "@odata.type": "Microsoft.Dynamics.CRM.MemoAttributeMetadata", - "SchemaName": column_schema_name, - "DisplayName": self._label(label), - "RequiredLevel": {"Value": "None"}, - "MaxLength": 4000, - "FormatName": {"Value": "Text"}, - "ImeMode": "Auto", - } - if dtype_l in ("int", "integer"): - return { - "@odata.type": "Microsoft.Dynamics.CRM.IntegerAttributeMetadata", - "SchemaName": column_schema_name, - "DisplayName": self._label(label), - "RequiredLevel": {"Value": "None"}, - "Format": "None", - "MinValue": -2147483648, - "MaxValue": 2147483647, - } - if dtype_l in ("decimal", "money"): - return { - "@odata.type": "Microsoft.Dynamics.CRM.DecimalAttributeMetadata", - "SchemaName": column_schema_name, - "DisplayName": self._label(label), - "RequiredLevel": {"Value": "None"}, - "MinValue": -100000000000.0, - "MaxValue": 100000000000.0, - "Precision": 2, - } - if dtype_l in ("float", "double"): - return { - "@odata.type": "Microsoft.Dynamics.CRM.DoubleAttributeMetadata", - "SchemaName": column_schema_name, - "DisplayName": self._label(label), - "RequiredLevel": {"Value": "None"}, - "MinValue": -100000000000.0, - "MaxValue": 100000000000.0, - "Precision": 2, - } - if dtype_l in ("datetime", "date"): - return { - "@odata.type": "Microsoft.Dynamics.CRM.DateTimeAttributeMetadata", - "SchemaName": column_schema_name, - "DisplayName": self._label(label), - "RequiredLevel": {"Value": "None"}, - "Format": "DateOnly", - "ImeMode": "Inactive", - } - if dtype_l in ("bool", "boolean"): - return { - "@odata.type": "Microsoft.Dynamics.CRM.BooleanAttributeMetadata", - "SchemaName": column_schema_name, - "DisplayName": self._label(label), - "RequiredLevel": {"Value": "None"}, - "OptionSet": { - "@odata.type": "Microsoft.Dynamics.CRM.BooleanOptionSetMetadata", - "TrueOption": { - "Value": 1, - "Label": self._label("True"), - }, - "FalseOption": { - "Value": 0, - "Label": self._label("False"), - }, - "IsGlobal": False, - }, - } - if dtype_l == "file": - return { - "@odata.type": "Microsoft.Dynamics.CRM.FileAttributeMetadata", - "SchemaName": column_schema_name, - "DisplayName": self._label(label), - "RequiredLevel": {"Value": "None"}, - } - return None - def _get_table_info(self, table_schema_name: str) -> Optional[Dict[str, Any]]: """Return basic metadata for a custom table if it exists. @@ -2388,210 +1770,6 @@ def _build_list( headers = {"Prefer": ",".join(prefer_parts)} if prefer_parts else None return _RawRequest(method="GET", url=url, headers=headers) - def _build_create_entity( - self, - table: str, - columns: Dict[str, Any], - solution: Optional[str] = None, - primary_column: Optional[str] = None, - display_name: Optional[str] = None, - ) -> _RawRequest: - """Build an EntityDefinitions POST request without sending it.""" - if primary_column: - primary_attr = primary_column - else: - primary_attr = f"{table.split('_', 1)[0]}_Name" if "_" in table else "new_Name" - attributes = [self._attribute_payload(primary_attr, "string", is_primary_name=True)] - for col_name, dtype in columns.items(): - attr = self._attribute_payload(col_name, dtype) - if not attr: - raise ValidationError( - f"Unsupported column type '{dtype}' for column '{col_name}'.", - subcode=VALIDATION_UNSUPPORTED_COLUMN_TYPE, - ) - attributes.append(attr) - if display_name is not None: - if not isinstance(display_name, str) or not display_name.strip(): - raise TypeError("display_name must be a non-empty string when provided") - label = display_name if display_name is not None else table - body = { - "@odata.type": "Microsoft.Dynamics.CRM.EntityMetadata", - "SchemaName": table, - "DisplayName": self._label(label), - "DisplayCollectionName": self._label(label + "s"), - "Description": self._label(f"Custom entity for {label}"), - "OwnershipType": "UserOwned", - "HasActivities": False, - "HasNotes": True, - "IsActivity": False, - "Attributes": attributes, - } - url = f"{self.api}/EntityDefinitions" - if solution: - url += f"?SolutionUniqueName={solution}" - return _RawRequest( - method="POST", - url=url, - body=json.dumps(body, ensure_ascii=False), - ) - - def _build_delete_entity(self, metadata_id: str) -> _RawRequest: - """Build an EntityDefinitions DELETE request without sending it.""" - return _RawRequest( - method="DELETE", - url=f"{self.api}/EntityDefinitions({metadata_id})", - headers={"If-Match": "*"}, - ) - - def _build_get_entity(self, table: str) -> _RawRequest: - """Build an EntityDefinitions GET request without sending it.""" - logical = self._escape_odata_quotes(table.lower()) - return _RawRequest( - method="GET", - url=( - f"{self.api}/EntityDefinitions" - f"?$select=MetadataId,LogicalName,SchemaName,EntitySetName,PrimaryNameAttribute,PrimaryIdAttribute" - f"&$filter=LogicalName eq '{logical}'" - ), - ) - - def _build_list_entities( - self, - *, - filter: Optional[str] = None, - select: Optional[List[str]] = None, - ) -> _RawRequest: - """Build an EntityDefinitions list GET request without sending it.""" - base_filter = "IsPrivate eq false" - if filter: - combined_filter = f"{base_filter} and ({filter})" - else: - combined_filter = base_filter - url = f"{self.api}/EntityDefinitions?$filter={combined_filter}" - if select is not None and isinstance(select, str): - raise TypeError("select must be a list of property names, not a bare string") - if select: - url += "&$select=" + ",".join(select) - return _RawRequest(method="GET", url=url) - - def _build_create_column( - self, - entity_metadata_id: str, - col_name: str, - dtype: Any, - ) -> _RawRequest: - """Build an Attributes POST request for one column without sending it.""" - attr = self._attribute_payload(col_name, dtype) - if not attr: - raise ValidationError( - f"Unsupported column type '{dtype}' for column '{col_name}'.", - subcode=VALIDATION_UNSUPPORTED_COLUMN_TYPE, - ) - return _RawRequest( - method="POST", - url=f"{self.api}/EntityDefinitions({entity_metadata_id})/Attributes", - body=json.dumps(attr, ensure_ascii=False), - ) - - def _build_delete_column( - self, - entity_metadata_id: str, - col_metadata_id: str, - ) -> _RawRequest: - """Build an Attributes DELETE request for one column without sending it.""" - return _RawRequest( - method="DELETE", - url=f"{self.api}/EntityDefinitions({entity_metadata_id})/Attributes({col_metadata_id})", - headers={"If-Match": "*"}, - ) - - @staticmethod - def _build_lookup_field_models( - referencing_table: str, - lookup_field_name: str, - referenced_table: str, - *, - display_name: Optional[str] = None, - description: Optional[str] = None, - required: bool = False, - cascade_delete: str = CASCADE_BEHAVIOR_REMOVE_LINK, - language_code: int = 1033, - ) -> tuple: - """Build a (lookup, relationship) pair for a lookup field creation. - - Returns ``(LookupAttributeMetadata, OneToManyRelationshipMetadata)``. - Used by both the batch resolver and ``TableOperations.create_lookup_field`` - to avoid duplicating the metadata assembly logic. - - Note: ``referencing_table`` and ``referenced_table`` are lowercased - automatically because Dataverse stores entity logical names in - lowercase. ``lookup_field_name`` is kept as-is (it is a SchemaName). - """ - # Dataverse logical names are always lowercase. Callers may pass - # SchemaName-cased values (e.g. "new_SQLTeam"); normalise here so - # the relationship metadata uses valid logical names. - referencing_lower = referencing_table.lower() - referenced_lower = referenced_table.lower() - - lookup = LookupAttributeMetadata( - schema_name=lookup_field_name, - display_name=Label( - localized_labels=[ - LocalizedLabel( - label=display_name or referenced_table, - language_code=language_code, - ) - ] - ), - required_level="ApplicationRequired" if required else "None", - ) - if description: - lookup.description = Label( - localized_labels=[LocalizedLabel(label=description, language_code=language_code)] - ) - rel_name = f"{referenced_lower}_{referencing_lower}_{lookup_field_name}" - relationship = OneToManyRelationshipMetadata( - schema_name=rel_name, - referenced_entity=referenced_lower, - referencing_entity=referencing_lower, - referenced_attribute=f"{referenced_lower}id", - cascade_configuration=CascadeConfiguration(delete=cascade_delete), - ) - return lookup, relationship - - def _build_create_relationship( - self, - body: Dict[str, Any], - *, - solution: Optional[str] = None, - ) -> _RawRequest: - """Build a RelationshipDefinitions POST request without sending it.""" - headers: Dict[str, str] = {} - if solution: - headers["MSCRM.SolutionUniqueName"] = solution - return _RawRequest( - method="POST", - url=f"{self.api}/RelationshipDefinitions", - body=json.dumps(body, ensure_ascii=False), - headers=headers or None, - ) - - def _build_delete_relationship(self, relationship_id: str) -> _RawRequest: - """Build a RelationshipDefinitions DELETE request without sending it.""" - return _RawRequest( - method="DELETE", - url=f"{self.api}/RelationshipDefinitions({relationship_id})", - headers={"If-Match": "*"}, - ) - - def _build_get_relationship(self, schema_name: str) -> _RawRequest: - """Build a RelationshipDefinitions GET request without sending it.""" - escaped = self._escape_odata_quotes(schema_name) - return _RawRequest( - method="GET", - url=f"{self.api}/RelationshipDefinitions?$filter=SchemaName eq '{escaped}'", - ) - def _build_sql(self, sql: str) -> _RawRequest: """Build a SQL query GET request without sending it. @@ -2613,27 +1791,3 @@ def _build_sql(self, sql: str) -> _RawRequest: method="GET", url=f"{self.api}/{entity_set}?sql={_url_quote(sql, safe='')}", ) - - # ---------------------- Cache maintenance ------------------------- - def _flush_cache( - self, - kind, - ) -> int: - """Flush cached client metadata/state. - - :param kind: Cache kind to flush (only ``"picklist"`` supported). - :type kind: ``str`` - :return: Number of cache entries removed. - :rtype: ``int`` - :raises ValidationError: If ``kind`` is unsupported. - """ - k = (kind or "").strip().lower() - if k != "picklist": - raise ValidationError( - f"Unsupported cache kind '{kind}' (only 'picklist' is implemented)", - subcode=VALIDATION_UNSUPPORTED_CACHE_KIND, - ) - - removed = len(self._picklist_label_cache) - self._picklist_label_cache.clear() - return removed diff --git a/src/PowerPlatform/Dataverse/data/_odata_base.py b/src/PowerPlatform/Dataverse/data/_odata_base.py new file mode 100644 index 00000000..34ff7c55 --- /dev/null +++ b/src/PowerPlatform/Dataverse/data/_odata_base.py @@ -0,0 +1,936 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +"""Shared pure-logic base for the Dataverse OData client. Contains no I/O. + +Subclasses add the HTTP transport layer (sync or async) while sharing all +URL construction, payload building, cache helpers, and other stateless logic. +""" + +from __future__ import annotations + +import json +import re +import unicodedata +import uuid +import warnings +from contextlib import contextmanager +from contextvars import ContextVar +from dataclasses import dataclass, field +from enum import Enum +from typing import Any, Callable, Dict, List, Optional, Union +from urllib.parse import parse_qs, urlparse + +from .. import __version__ as _SDK_VERSION + +from ..core.errors import ValidationError +from ..core._error_codes import ( + VALIDATION_UNSUPPORTED_COLUMN_TYPE, + VALIDATION_UNSUPPORTED_CACHE_KIND, + VALIDATION_SQL_WRITE_BLOCKED, + VALIDATION_SQL_UNSUPPORTED_SYNTAX, +) +from ..models.relationship import ( + LookupAttributeMetadata, + OneToManyRelationshipMetadata, + CascadeConfiguration, +) +from ..models.labels import Label, LocalizedLabel +from ..common.constants import CASCADE_BEHAVIOR_REMOVE_LINK +from ._raw_request import _RawRequest + +__all__ = [] + +_GUID_RE = re.compile(r"[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}") +_CALL_SCOPE_CORRELATION_ID: ContextVar[Optional[str]] = ContextVar("_CALL_SCOPE_CORRELATION_ID", default=None) +_USER_AGENT = f"DataverseSvcPythonClient:{_SDK_VERSION}" +_DEFAULT_EXPECTED_STATUSES: tuple[int, ...] = (200, 201, 202, 204) + + +def _extract_pagingcookie(next_link: str) -> Optional[str]: + """Extract the raw pagingcookie value from a SQL ``@odata.nextLink`` URL. + + The Dataverse SQL endpoint has a server-side bug where the pagingcookie + (containing first/last record GUIDs) does not advance between pages even + though ``pagenumber`` increments. Detecting a repeated cookie lets the + pagination loop break instead of looping indefinitely. + + Returns the pagingcookie string if present, or ``None`` if not found. + """ + try: + qs = parse_qs(urlparse(next_link).query) + skiptoken = qs.get("$skiptoken", [None])[0] + if not skiptoken: + return None + # parse_qs already URL-decodes the value once, giving the outer XML with + # pagingcookie still percent-encoded (e.g. pagingcookie="%3ccookie..."). + # A second decode is intentionally omitted: decoding again would turn %22 + # into " inside the cookie XML, breaking the regex and causing every page + # to extract the same truncated prefix regardless of the actual GUIDs. + m = re.search(r'pagingcookie="([^"]+)"', skiptoken) + if m: + return m.group(1) + except Exception: + pass + return None + + +@dataclass +class _RequestContext: + """Structured request context used by ``_request`` to clarify payload and metadata.""" + + method: str + url: str + expected: tuple[int, ...] = _DEFAULT_EXPECTED_STATUSES + headers: Optional[Dict[str, str]] = None + kwargs: Dict[str, Any] = field(default_factory=dict) + + @classmethod + def build( + cls, + method: str, + url: str, + *, + expected: tuple[int, ...] = _DEFAULT_EXPECTED_STATUSES, + merge_headers: Optional[Callable[[Optional[Dict[str, str]]], Dict[str, str]]] = None, + **kwargs: Any, + ) -> "_RequestContext": + headers = kwargs.get("headers") + headers = merge_headers(headers) if merge_headers else (headers or {}) + headers.setdefault("x-ms-client-request-id", str(uuid.uuid4())) + headers.setdefault("x-ms-correlation-id", _CALL_SCOPE_CORRELATION_ID.get()) + kwargs["headers"] = headers + return cls( + method=method, + url=url, + expected=expected, + headers=headers, + kwargs=kwargs or {}, + ) + + +class _ODataBase: + """Pure-logic base for the Dataverse OData client. + + Provides URL construction, cache management, payload builders, and other + stateless or cache-only helpers. No I/O is performed here; subclasses + must supply ``_request`` and the rest of the HTTP transport layer. + """ + + def __init__(self, base_url: str, config=None) -> None: + """Initialise shared state: URL, API root, config, in-memory caches, and HTTP logger. + + :param base_url: Organisation base URL (e.g. ``"https://.crm.dynamics.com"``). + :type base_url: :class:`str` + :param config: Optional Dataverse configuration (HTTP retry, backoff, timeout, + language code, HTTP diagnostic logging). If omitted, ``DataverseConfig.from_env()`` is used. + :type config: ~PowerPlatform.Dataverse.core.config.DataverseConfig or None + :raises ValueError: If ``base_url`` is empty after stripping. + """ + self.base_url = (base_url or "").rstrip("/") + if not self.base_url: + raise ValueError("base_url is required.") + self.api = f"{self.base_url}/api/data/v9.2" + self.config = ( + config + or __import__( + "PowerPlatform.Dataverse.core.config", fromlist=["DataverseConfig"] + ).DataverseConfig.from_env() + ) + self._logical_to_entityset_cache: dict[str, str] = {} + # Cache: normalized table_schema_name (lowercase) -> primary id attribute (e.g. accountid) + self._logical_primaryid_cache: dict[str, str] = {} + self._picklist_label_cache: dict[str, dict] = {} + self._picklist_cache_ttl_seconds = 3600 # 1 hour TTL + ctx_obj = self.config.operation_context + self._operation_context: Optional[str] = ctx_obj.user_agent_context if ctx_obj else None + self._http_logger = None + if self.config.log_config is not None: + from ..core._http_logger import _HttpLogger + + self._http_logger = _HttpLogger(self.config.log_config) + + # ------------------------------------------------------------------ + # Static helpers + # ------------------------------------------------------------------ + + @staticmethod + def _escape_odata_quotes(value: str) -> str: + """Escape single quotes for OData queries (by doubling them).""" + return value.replace("'", "''") + + @staticmethod + def _normalize_cache_key(table_schema_name: str) -> str: + """Normalize table_schema_name to lowercase for case-insensitive cache keys.""" + return table_schema_name.lower() if isinstance(table_schema_name, str) else "" + + @staticmethod + def _lowercase_keys(record: Dict[str, Any]) -> Dict[str, Any]: + """Convert all 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. + + Keys containing ``@odata.`` (e.g. ``new_CustomerId@odata.bind``) are + preserved as-is because the navigation property portion before ``@`` + must retain its original casing (case-sensitive navigation property name). The OData + parser validates ``@odata.bind`` property names **case-sensitively** + against the entity's declared navigation properties, so lowercasing + these keys causes ``400 - undeclared property`` errors. + """ + if not isinstance(record, dict): + return record + return {k.lower() if isinstance(k, str) and "@odata." not in k else k: v for k, v in record.items()} + + @staticmethod + def _lowercase_list(items: Optional[List[str]]) -> Optional[List[str]]: + """Convert all strings in a list to lowercase for case-insensitive column names. + + Used for $select and $orderby parameters where column names must be lowercase. + """ + if not items: + return items + return [item.lower() if isinstance(item, str) else item for item in items] + + @staticmethod + def _extract_logical_table(sql: str) -> str: + """Extract the logical table name after the first standalone FROM. + + Examples: + SELECT * FROM account + SELECT col1, startfrom FROM new_sampleitem WHERE col1 = 1 + + """ + if not isinstance(sql, str): + raise ValueError("sql must be a string") + # Mask out single-quoted string literals to avoid matching FROM inside them. + masked = re.sub(r"'([^']|'')*'", "'x'", sql) + pattern = r"\bfrom\b\s+([A-Za-z0-9_]+)" # minimal, single-line regex + m = re.search(pattern, masked, flags=re.IGNORECASE) + if not m: + raise ValueError("Unable to determine table logical name from SQL (expected 'FROM ').") + return m.group(1).lower() + + # ------------------------------------------------------------------ + # Instance helpers + # ------------------------------------------------------------------ + + @contextmanager + def _call_scope(self): + """Context manager to generate a new correlation id for each SDK call scope.""" + shared_id = str(uuid.uuid4()) + token = _CALL_SCOPE_CORRELATION_ID.set(shared_id) + try: + yield shared_id + finally: + _CALL_SCOPE_CORRELATION_ID.reset(token) + + def _format_key(self, key: str) -> str: + k = key.strip() + if k.startswith("(") and k.endswith(")"): + return k + # Escape single quotes in alternate key values + if "=" in k and "'" in k: + + def esc(match): + # match.group(1) is the key, match.group(2) is the value + return f"{match.group(1)}='{self._escape_odata_quotes(match.group(2))}'" + + k = re.sub(r"(\w+)=\'([^\']*)\'", esc, k) + return f"({k})" + if len(k) == 36 and "-" in k: + return f"({k})" + return f"({k})" + + def _build_alternate_key_str(self, alternate_key: Dict[str, Any]) -> str: + """Build an OData alternate key segment from a mapping of key names to values. + + String values are single-quoted and escaped; all other values are rendered as-is. + + :param alternate_key: Mapping of alternate key attribute names to their values. + Must be a non-empty dict with string keys. + :type alternate_key: ``dict[str, Any]`` + + :return: Comma-separated key=value pairs suitable for use in a URL segment. + :rtype: ``str`` + + :raises ValueError: If ``alternate_key`` is empty. + :raises TypeError: If any key in ``alternate_key`` is not a string. + """ + if not alternate_key: + raise ValueError("alternate_key must be a non-empty dict") + bad_keys = [k for k in alternate_key if not isinstance(k, str)] + if bad_keys: + raise TypeError(f"alternate_key keys must be strings; got: {bad_keys!r}") + parts = [] + for k, v in alternate_key.items(): + k_lower = k.lower() if isinstance(k, str) else k + if isinstance(v, str): + v_escaped = self._escape_odata_quotes(v) + parts.append(f"{k_lower}='{v_escaped}'") + else: + parts.append(f"{k_lower}={v}") + return ",".join(parts) + + def _label(self, text: str) -> Dict[str, Any]: + lang = int(self.config.language_code) + return { + "@odata.type": "Microsoft.Dynamics.CRM.Label", + "LocalizedLabels": [ + { + "@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel", + "Label": text, + "LanguageCode": lang, + } + ], + } + + def _to_pascal(self, name: str) -> str: + parts = re.split(r"[^A-Za-z0-9]+", name) + return "".join(p[:1].upper() + p[1:] for p in parts if p) + + def _normalize_picklist_label(self, label: str) -> str: + """Normalize a label for case / diacritic insensitive comparison.""" + if not isinstance(label, str): + return "" + # Strip accents + norm = unicodedata.normalize("NFD", label) + norm = "".join(c for c in norm if unicodedata.category(c) != "Mn") + # Collapse whitespace, lowercase + norm = re.sub(r"\s+", " ", norm).strip().lower() + return norm + + # ------------------------------------------------------------------ + # Payload builders (no I/O) + # ------------------------------------------------------------------ + + def _build_localizedlabels_payload(self, translations: Dict[int, str]) -> Dict[str, Any]: + """Build a Dataverse Label object from {: } entries. + + Ensures at least one localized label. Does not deduplicate language codes; last wins. + """ + locs: List[Dict[str, Any]] = [] + for lang, text in translations.items(): + if not isinstance(lang, int): + raise ValueError(f"Language code '{lang}' must be int") + if not isinstance(text, str) or not text.strip(): + raise ValueError(f"Label for lang {lang} must be non-empty string") + locs.append( + { + "@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel", + "Label": text, + "LanguageCode": lang, + } + ) + if not locs: + raise ValueError("At least one translation required") + return { + "@odata.type": "Microsoft.Dynamics.CRM.Label", + "LocalizedLabels": locs, + } + + def _enum_optionset_payload( + self, column_schema_name: str, enum_cls: type[Enum], is_primary_name: bool = False + ) -> Dict[str, Any]: + """Create local (IsGlobal=False) PicklistAttributeMetadata from an Enum subclass. + + Supports translation mapping via optional class attribute `__labels__`: + __labels__ = { 1033: { "Active": "Active", "Inactive": "Inactive" }, + 1036: { "Active": "Actif", "Inactive": "Inactif" } } + + Keys inside per-language dict may be either enum member objects or their names. + If a language lacks a label for a member, member.name is used as fallback. + The client's configured language code is always ensured to exist. + """ + all_member_items = list(enum_cls.__members__.items()) + if not all_member_items: + raise ValueError(f"Enum {enum_cls.__name__} has no members") + + # Duplicate detection + value_to_first_name: Dict[int, str] = {} + for name, member in all_member_items: + val = getattr(member, "value", None) + # Defer non-int validation to later loop for consistency + if val in value_to_first_name and value_to_first_name[val] != name: + raise ValueError( + f"Duplicate enum value {val} in {enum_cls.__name__} (names: {value_to_first_name[val]}, {name})" + ) + value_to_first_name[val] = name + + members = list(enum_cls) + # Validate integer values + for m in members: + if not isinstance(m.value, int): + raise ValueError(f"Enum member '{m.name}' has non-int value '{m.value}' (only int values supported)") + + raw_labels = getattr(enum_cls, "__labels__", None) + labels_by_lang: Dict[int, Dict[str, str]] = {} + if raw_labels is not None: + if not isinstance(raw_labels, dict): + raise ValueError("__labels__ must be a dict {lang:int -> {member: label}}") + # Build a helper map for value -> member name to resolve raw int keys + value_to_name = {m.value: m.name for m in members} + for lang, mapping in raw_labels.items(): + if not isinstance(lang, int): + raise ValueError("Language codes in __labels__ must be ints") + if not isinstance(mapping, dict): + raise ValueError(f"__labels__[{lang}] must be a dict of member names to strings") + labels_by_lang.setdefault(lang, {}) + for k, v in mapping.items(): + # Accept enum member object, its name, or raw int value (from class body reference) + if isinstance(k, enum_cls): + member_name = k.name + elif isinstance(k, int): + member_name = value_to_name.get(k) + if member_name is None: + raise ValueError(f"__labels__[{lang}] has int key {k} not matching any enum value") + else: + member_name = str(k) + if not isinstance(v, str) or not v.strip(): + raise ValueError(f"Label for {member_name} lang {lang} must be non-empty string") + labels_by_lang[lang][member_name] = v + + config_lang = int(self.config.language_code) + # Ensure config language appears (fallback to names) + all_langs = set(labels_by_lang.keys()) | {config_lang} + + options: List[Dict[str, Any]] = [] + for m in sorted(members, key=lambda x: x.value): + per_lang: Dict[int, str] = {} + for lang in all_langs: + label_text = labels_by_lang.get(lang, {}).get(m.name, m.name) + per_lang[lang] = label_text + options.append( + { + "@odata.type": "Microsoft.Dynamics.CRM.OptionMetadata", + "Value": m.value, + "Label": self._build_localizedlabels_payload(per_lang), + } + ) + + attr_label = column_schema_name.split("_")[-1] + return { + "@odata.type": "Microsoft.Dynamics.CRM.PicklistAttributeMetadata", + "SchemaName": column_schema_name, + "DisplayName": self._label(attr_label), + "RequiredLevel": {"Value": "None"}, + "IsPrimaryName": bool(is_primary_name), + "OptionSet": { + "@odata.type": "Microsoft.Dynamics.CRM.OptionSetMetadata", + "IsGlobal": False, + "Options": options, + }, + } + + def _attribute_payload( + self, column_schema_name: str, dtype: Any, *, is_primary_name: bool = False + ) -> Optional[Dict[str, Any]]: + # Enum-based local option set support + if isinstance(dtype, type) and issubclass(dtype, Enum): + return self._enum_optionset_payload(column_schema_name, dtype, is_primary_name=is_primary_name) + if not isinstance(dtype, str): + raise ValueError( + f"Unsupported column spec type for '{column_schema_name}': {type(dtype)} (expected str or Enum subclass)" + ) + dtype_l = dtype.lower().strip() + label = column_schema_name.split("_")[-1] + if dtype_l in ("string", "text"): + return { + "@odata.type": "Microsoft.Dynamics.CRM.StringAttributeMetadata", + "SchemaName": column_schema_name, + "DisplayName": self._label(label), + "RequiredLevel": {"Value": "None"}, + "MaxLength": 200, + "FormatName": {"Value": "Text"}, + "IsPrimaryName": bool(is_primary_name), + } + if dtype_l in ("memo", "multiline"): + return { + "@odata.type": "Microsoft.Dynamics.CRM.MemoAttributeMetadata", + "SchemaName": column_schema_name, + "DisplayName": self._label(label), + "RequiredLevel": {"Value": "None"}, + "MaxLength": 4000, + "FormatName": {"Value": "Text"}, + "ImeMode": "Auto", + } + if dtype_l in ("int", "integer"): + return { + "@odata.type": "Microsoft.Dynamics.CRM.IntegerAttributeMetadata", + "SchemaName": column_schema_name, + "DisplayName": self._label(label), + "RequiredLevel": {"Value": "None"}, + "Format": "None", + "MinValue": -2147483648, + "MaxValue": 2147483647, + } + if dtype_l in ("decimal", "money"): + return { + "@odata.type": "Microsoft.Dynamics.CRM.DecimalAttributeMetadata", + "SchemaName": column_schema_name, + "DisplayName": self._label(label), + "RequiredLevel": {"Value": "None"}, + "MinValue": -100000000000.0, + "MaxValue": 100000000000.0, + "Precision": 2, + } + if dtype_l in ("float", "double"): + return { + "@odata.type": "Microsoft.Dynamics.CRM.DoubleAttributeMetadata", + "SchemaName": column_schema_name, + "DisplayName": self._label(label), + "RequiredLevel": {"Value": "None"}, + "MinValue": -100000000000.0, + "MaxValue": 100000000000.0, + "Precision": 2, + } + if dtype_l in ("datetime", "date"): + return { + "@odata.type": "Microsoft.Dynamics.CRM.DateTimeAttributeMetadata", + "SchemaName": column_schema_name, + "DisplayName": self._label(label), + "RequiredLevel": {"Value": "None"}, + "Format": "DateOnly", + "ImeMode": "Inactive", + } + if dtype_l in ("bool", "boolean"): + return { + "@odata.type": "Microsoft.Dynamics.CRM.BooleanAttributeMetadata", + "SchemaName": column_schema_name, + "DisplayName": self._label(label), + "RequiredLevel": {"Value": "None"}, + "OptionSet": { + "@odata.type": "Microsoft.Dynamics.CRM.BooleanOptionSetMetadata", + "TrueOption": { + "Value": 1, + "Label": self._label("True"), + }, + "FalseOption": { + "Value": 0, + "Label": self._label("False"), + }, + "IsGlobal": False, + }, + } + if dtype_l == "file": + return { + "@odata.type": "Microsoft.Dynamics.CRM.FileAttributeMetadata", + "SchemaName": column_schema_name, + "DisplayName": self._label(label), + "RequiredLevel": {"Value": "None"}, + } + return None + + # ------------------------------------------------------------------ + # Entity / column / relationship _build_* methods (no I/O) + # ------------------------------------------------------------------ + + def _build_create_entity( + self, + table: str, + columns: Dict[str, Any], + solution: Optional[str] = None, + primary_column: Optional[str] = None, + display_name: Optional[str] = None, + ) -> _RawRequest: + """Build an EntityDefinitions POST request without sending it.""" + if primary_column: + primary_attr = primary_column + else: + primary_attr = f"{table.split('_', 1)[0]}_Name" if "_" in table else "new_Name" + attributes = [self._attribute_payload(primary_attr, "string", is_primary_name=True)] + for col_name, dtype in columns.items(): + attr = self._attribute_payload(col_name, dtype) + if not attr: + raise ValidationError( + f"Unsupported column type '{dtype}' for column '{col_name}'.", + subcode=VALIDATION_UNSUPPORTED_COLUMN_TYPE, + ) + attributes.append(attr) + if display_name is not None: + if not isinstance(display_name, str) or not display_name.strip(): + raise TypeError("display_name must be a non-empty string when provided") + label = display_name if display_name is not None else table + body = { + "@odata.type": "Microsoft.Dynamics.CRM.EntityMetadata", + "SchemaName": table, + "DisplayName": self._label(label), + "DisplayCollectionName": self._label(label + "s"), + "Description": self._label(f"Custom entity for {label}"), + "OwnershipType": "UserOwned", + "HasActivities": False, + "HasNotes": True, + "IsActivity": False, + "Attributes": attributes, + } + url = f"{self.api}/EntityDefinitions" + if solution: + url += f"?SolutionUniqueName={solution}" + return _RawRequest( + method="POST", + url=url, + body=json.dumps(body, ensure_ascii=False), + ) + + def _build_delete_entity(self, metadata_id: str) -> _RawRequest: + """Build an EntityDefinitions DELETE request without sending it.""" + return _RawRequest( + method="DELETE", + url=f"{self.api}/EntityDefinitions({metadata_id})", + headers={"If-Match": "*"}, + ) + + def _build_get_entity(self, table: str) -> _RawRequest: + """Build an EntityDefinitions GET request without sending it.""" + logical = self._escape_odata_quotes(table.lower()) + return _RawRequest( + method="GET", + url=( + f"{self.api}/EntityDefinitions" + f"?$select=MetadataId,LogicalName,SchemaName,EntitySetName,PrimaryNameAttribute,PrimaryIdAttribute" + f"&$filter=LogicalName eq '{logical}'" + ), + ) + + def _build_list_entities( + self, + *, + filter: Optional[str] = None, + select: Optional[List[str]] = None, + ) -> _RawRequest: + """Build an EntityDefinitions list GET request without sending it.""" + base_filter = "IsPrivate eq false" + if filter: + combined_filter = f"{base_filter} and ({filter})" + else: + combined_filter = base_filter + url = f"{self.api}/EntityDefinitions?$filter={combined_filter}" + if select is not None and isinstance(select, str): + raise TypeError("select must be a list of property names, not a bare string") + if select: + url += "&$select=" + ",".join(select) + return _RawRequest(method="GET", url=url) + + def _build_create_column( + self, + entity_metadata_id: str, + col_name: str, + dtype: Any, + ) -> _RawRequest: + """Build an Attributes POST request for one column without sending it.""" + attr = self._attribute_payload(col_name, dtype) + if not attr: + raise ValidationError( + f"Unsupported column type '{dtype}' for column '{col_name}'.", + subcode=VALIDATION_UNSUPPORTED_COLUMN_TYPE, + ) + return _RawRequest( + method="POST", + url=f"{self.api}/EntityDefinitions({entity_metadata_id})/Attributes", + body=json.dumps(attr, ensure_ascii=False), + ) + + def _build_delete_column( + self, + entity_metadata_id: str, + col_metadata_id: str, + ) -> _RawRequest: + """Build an Attributes DELETE request for one column without sending it.""" + return _RawRequest( + method="DELETE", + url=f"{self.api}/EntityDefinitions({entity_metadata_id})/Attributes({col_metadata_id})", + headers={"If-Match": "*"}, + ) + + @staticmethod + def _build_lookup_field_models( + referencing_table: str, + lookup_field_name: str, + referenced_table: str, + *, + display_name: Optional[str] = None, + description: Optional[str] = None, + required: bool = False, + cascade_delete: str = CASCADE_BEHAVIOR_REMOVE_LINK, + language_code: int = 1033, + ) -> tuple: + """Build a (lookup, relationship) pair for a lookup field creation. + + Returns ``(LookupAttributeMetadata, OneToManyRelationshipMetadata)``. + Used by both the batch resolver and ``TableOperations.create_lookup_field`` + to avoid duplicating the metadata assembly logic. + + Note: ``referencing_table`` and ``referenced_table`` are lowercased + automatically because Dataverse stores entity logical names in + lowercase. ``lookup_field_name`` is kept as-is (it is a SchemaName). + """ + # Dataverse logical names are always lowercase. Callers may pass + # SchemaName-cased values (e.g. "new_SQLTeam"); normalise here so + # the relationship metadata uses valid logical names. + referencing_lower = referencing_table.lower() + referenced_lower = referenced_table.lower() + + lookup = LookupAttributeMetadata( + schema_name=lookup_field_name, + display_name=Label( + localized_labels=[ + LocalizedLabel( + label=display_name or referenced_table, + language_code=language_code, + ) + ] + ), + required_level="ApplicationRequired" if required else "None", + ) + if description: + lookup.description = Label( + localized_labels=[LocalizedLabel(label=description, language_code=language_code)] + ) + rel_name = f"{referenced_lower}_{referencing_lower}_{lookup_field_name}" + relationship = OneToManyRelationshipMetadata( + schema_name=rel_name, + referenced_entity=referenced_lower, + referencing_entity=referencing_lower, + referenced_attribute=f"{referenced_lower}id", + cascade_configuration=CascadeConfiguration(delete=cascade_delete), + ) + return lookup, relationship + + def _build_create_relationship( + self, + body: Dict[str, Any], + *, + solution: Optional[str] = None, + ) -> _RawRequest: + """Build a RelationshipDefinitions POST request without sending it.""" + headers: Dict[str, str] = {} + if solution: + headers["MSCRM.SolutionUniqueName"] = solution + return _RawRequest( + method="POST", + url=f"{self.api}/RelationshipDefinitions", + body=json.dumps(body, ensure_ascii=False), + headers=headers or None, + ) + + def _build_delete_relationship(self, relationship_id: str) -> _RawRequest: + """Build a RelationshipDefinitions DELETE request without sending it.""" + return _RawRequest( + method="DELETE", + url=f"{self.api}/RelationshipDefinitions({relationship_id})", + headers={"If-Match": "*"}, + ) + + def _build_get_relationship(self, schema_name: str) -> _RawRequest: + """Build a RelationshipDefinitions GET request without sending it.""" + escaped = self._escape_odata_quotes(schema_name) + return _RawRequest( + method="GET", + url=f"{self.api}/RelationshipDefinitions?$filter=SchemaName eq '{escaped}'", + ) + + # ------------------------------------------------------------------ + # SQL guardrails + # ------------------------------------------------------------------ + + # ----------------------- SQL guardrail patterns -------------------- + _SQL_WRITE_RE = re.compile( + r"^\s*(?:INSERT|UPDATE|DELETE|DROP|TRUNCATE|ALTER|CREATE|EXEC|GRANT|REVOKE|BULK)\b", + re.IGNORECASE, + ) + _SQL_COMMENT_RE = re.compile(r"/\*[^*]*\*+(?:[^/*][^*]*\*+)*/|--[^\n]*", re.DOTALL) + _SQL_LEADING_WILDCARD_RE = re.compile(r"\bLIKE\s+'%[^']", re.IGNORECASE) + _SQL_IMPLICIT_CROSS_JOIN_RE = re.compile( + r"\bFROM\s+[A-Za-z0-9_]+(?:\s+[A-Za-z0-9_]+)?\s*,\s*[A-Za-z0-9_]+", + re.IGNORECASE, + ) + # Server-blocked SQL patterns (save the round-trip by catching early) + _SQL_UNSUPPORTED_JOIN_RE = re.compile( + r"\b(?:CROSS\s+JOIN|RIGHT\s+(?:OUTER\s+)?JOIN|FULL\s+(?:OUTER\s+)?JOIN)\b", + re.IGNORECASE, + ) + _SQL_UNION_RE = re.compile(r"\bUNION\b", re.IGNORECASE) + _SQL_HAVING_RE = re.compile(r"\bHAVING\b", re.IGNORECASE) + _SQL_CTE_RE = re.compile(r"^\s*WITH\b", re.IGNORECASE) + _SQL_SUBQUERY_RE = re.compile( + r"\bIN\s*\(\s*SELECT\b|\bEXISTS\s*\(\s*SELECT\b|\(\s*SELECT\b.*\bFROM\b", + re.IGNORECASE, + ) + # SELECT * is intentionally rejected -- not a technical limitation but a + # deliberate design decision. Wide entities (e.g. account has 307 columns) + # make SELECT * extremely expensive on shared database infrastructure. + # COUNT(*) is NOT matched because COUNT appears before the *. + _SQL_SELECT_STAR_RE = re.compile( + r"\bSELECT\b\s+(?:DISTINCT\s+)?(?:TOP\s+\d+(?:\s+PERCENT)?\s+)?\*\s", + re.IGNORECASE, + ) + + def _sql_guardrails(self, sql: str) -> str: + """Apply safety guardrails to a SQL query before sending to the server. + + Checks split into two categories: + + **Blocked** (``ValidationError`` -- saves a server round-trip): + + 1. Write statements (INSERT/UPDATE/DELETE/DROP/etc.) + 2. CROSS JOIN, RIGHT JOIN, FULL OUTER JOIN (server rejects these) + 3. UNION / UNION ALL (server rejects) + 4. HAVING clause (server rejects) + 5. CTE / WITH clause (server rejects) + 6. Subqueries -- IN (SELECT ...), EXISTS (SELECT ...) (server rejects) + 7. SELECT * -- intentional design decision, not a technical limitation. + Wide entities make wildcard selects extremely expensive on shared + database infrastructure. ``COUNT(*)`` is not affected. + + **Warned** (``UserWarning`` -- query still executes): + + 8. Leading-wildcard LIKE (full table scan) + 9. Implicit cross join FROM a, b (cartesian product) + + All blocked patterns are also blocked by the server, but catching + them here saves the network round-trip and provides clearer error + messages. To bypass a specific check (e.g., if the server adds + support in the future), all checks are in this single method. + + :param sql: The SQL string (already stripped). + :return: The SQL string (unchanged). + :raises ValidationError: If the SQL contains a blocked pattern. + """ + # --- BLOCKED (save server round-trip) --- + + # 1. Block writes (strip SQL comments first to catch comment-prefixed writes) + sql_no_comments = self._SQL_COMMENT_RE.sub(" ", sql).strip() + if self._SQL_WRITE_RE.search(sql_no_comments): + raise ValidationError( + "SQL endpoint is read-only. Use client.records or " + "client.dataframe for write operations " + "(INSERT/UPDATE/DELETE are not supported).", + subcode=VALIDATION_SQL_WRITE_BLOCKED, + ) + + # 2. Block unsupported JOIN types + m = self._SQL_UNSUPPORTED_JOIN_RE.search(sql) + if m: + raise ValidationError( + f"Unsupported JOIN type: '{m.group(0).strip()}'. " + "Only INNER JOIN and LEFT JOIN are supported by the " + "Dataverse SQL endpoint.", + subcode=VALIDATION_SQL_UNSUPPORTED_SYNTAX, + ) + + # 3. Block UNION + if self._SQL_UNION_RE.search(sql): + raise ValidationError( + "UNION is not supported by the Dataverse SQL endpoint. " + "Execute separate queries and combine results in Python " + "(e.g. pd.concat([df1, df2])).", + subcode=VALIDATION_SQL_UNSUPPORTED_SYNTAX, + ) + + # 4. Block HAVING + if self._SQL_HAVING_RE.search(sql): + raise ValidationError( + "HAVING is not supported by the Dataverse SQL endpoint. " + "Use WHERE to filter before GROUP BY instead.", + subcode=VALIDATION_SQL_UNSUPPORTED_SYNTAX, + ) + + # 5. Block CTE / WITH + if self._SQL_CTE_RE.search(sql): + raise ValidationError( + "CTE (WITH ... AS) is not supported by the Dataverse SQL " + "endpoint. Use separate queries and combine in Python.", + subcode=VALIDATION_SQL_UNSUPPORTED_SYNTAX, + ) + + # 6. Block subqueries + if self._SQL_SUBQUERY_RE.search(sql): + raise ValidationError( + "Subqueries are not supported by the Dataverse SQL " + "endpoint. Use separate SQL calls and combine results " + "in Python (e.g. step 1: get IDs, step 2: WHERE IN).", + subcode=VALIDATION_SQL_UNSUPPORTED_SYNTAX, + ) + + # 7. Block SELECT * -- intentional design decision. + # Wide entities (e.g. account has 307 columns) make wildcard selects + # extremely expensive on shared database infrastructure. + # COUNT(*) is NOT matched: _SQL_SELECT_STAR_RE requires * to be the + # first token after SELECT/DISTINCT/TOP N, so COUNT appears before *. + if self._SQL_SELECT_STAR_RE.search(sql): + raise ValidationError( + "SELECT * is not supported. Specify column names explicitly " + "(e.g. SELECT name, revenue FROM account). " + "Use client.query.sql_columns('account') to discover available columns.", + subcode=VALIDATION_SQL_UNSUPPORTED_SYNTAX, + ) + + # --- WARNED (query still executes) --- + + # 8. Warn on leading-wildcard LIKE + if self._SQL_LEADING_WILDCARD_RE.search(sql): + warnings.warn( + "Query contains a leading-wildcard LIKE pattern " + "(e.g. LIKE '%value'). This forces a full table scan " + "and may degrade performance on large tables. " + "Prefer trailing wildcards (LIKE 'value%') when possible.", + UserWarning, + stacklevel=4, + ) + + # 9. Warn on implicit cross joins (server allows but risky) + if self._SQL_IMPLICIT_CROSS_JOIN_RE.search(sql): + warnings.warn( + "Query uses an implicit cross join (FROM table1, table2). " + "This produces a cartesian product that can generate " + "millions of intermediate rows and degrade shared database " + "performance. Use explicit JOIN...ON syntax instead: " + "FROM table1 a JOIN table2 b ON a.column = b.column", + UserWarning, + stacklevel=4, + ) + + return sql + + # ------------------------------------------------------------------ + # Lifecycle + # ------------------------------------------------------------------ + + def close(self) -> None: + """Clear in-memory caches and close the HTTP diagnostic logger. + + Called by subclass ``close()`` via ``super()``. Safe to call multiple times. + """ + self._logical_to_entityset_cache.clear() + self._logical_primaryid_cache.clear() + self._picklist_label_cache.clear() + if self._http_logger is not None: + self._http_logger.close() + self._http_logger = None + + # ------------------------------------------------------------------ + # Cache maintenance + # ------------------------------------------------------------------ + + def _flush_cache( + self, + kind, + ) -> int: + """Flush cached client metadata/state. + + :param kind: Cache kind to flush (only ``"picklist"`` supported). + :type kind: ``str`` + :return: Number of cache entries removed. + :rtype: ``int`` + :raises ValidationError: If ``kind`` is unsupported. + """ + k = (kind or "").strip().lower() + if k != "picklist": + raise ValidationError( + f"Unsupported cache kind '{kind}' (only 'picklist' is implemented)", + subcode=VALIDATION_UNSUPPORTED_CACHE_KIND, + ) + + removed = len(self._picklist_label_cache) + self._picklist_label_cache.clear() + return removed diff --git a/src/PowerPlatform/Dataverse/models/query_builder.py b/src/PowerPlatform/Dataverse/models/query_builder.py index e7044d2e..bb2664fe 100644 --- a/src/PowerPlatform/Dataverse/models/query_builder.py +++ b/src/PowerPlatform/Dataverse/models/query_builder.py @@ -49,8 +49,18 @@ from __future__ import annotations +import sys import warnings -from typing import Any, Dict, Iterator, List, Optional, TypedDict, Union +from typing import Any, Iterator, List, Optional, TypedDict, Union + +# typing.Self (PEP 673, Python 3.11+) makes fluent methods return the concrete +# subclass type. TypeVar fallback for Python 3.10 uses the same name so docs render identically. +if sys.version_info >= (3, 11): + from typing import Self +else: + from typing import TypeVar + + Self = TypeVar("Self", bound="_QueryBuilderBase") # type: ignore[assignment] import pandas as pd @@ -171,29 +181,17 @@ def to_odata(self) -> str: return self.relation -class QueryBuilder: - """Fluent interface for building OData queries. - - Provides method chaining for constructing complex queries with - composable filter expressions. Can be used standalone (via :meth:`build`) - or bound to a client (via :meth:`execute`). - - :param table: Table schema name to query. - :type table: str - :raises ValueError: If ``table`` is empty. +class _QueryBuilderBase: + """Pure fluent interface for building OData queries — no I/O. - Example: - Standalone query construction:: + Holds all query state and chaining methods (``select``, ``where``, + ``order_by``, ``top``, ``page_size``, ``count``, ``expand``, + ``include_annotations``, ``include_formatted_values``) and + :meth:`build`. - from PowerPlatform.Dataverse.models.filters import col - - query = (QueryBuilder("account") - .select("name") - .where(col("statecode") == 0) - .top(10)) - params = query.build() - # {"table": "account", "select": ["name"], - # "filter": "statecode eq 0", "top": 10} + Subclasses add execution: :class:`QueryBuilder` for sync clients, + :class:`~PowerPlatform.Dataverse.aio.models.async_query_builder.AsyncQueryBuilder` + for async clients. """ def __init__(self, table: str) -> None: @@ -213,7 +211,7 @@ def __init__(self, table: str) -> None: # ----------------------------------------------------------------- select - def select(self, *columns: str) -> QueryBuilder: + def select(self, *columns: str) -> Self: """Select specific columns to retrieve. Column names are passed as-is; the OData layer lowercases them @@ -231,7 +229,7 @@ def select(self, *columns: str) -> QueryBuilder: # ------------------------------------------------------ filter: expression tree - def where(self, expression: filters.FilterExpression) -> QueryBuilder: + def where(self, expression: filters.FilterExpression) -> Self: """Add a composable filter expression. Accepts a :class:`~PowerPlatform.Dataverse.models.filters.FilterExpression` @@ -260,7 +258,7 @@ def where(self, expression: filters.FilterExpression) -> QueryBuilder: # --------------------------------------------------------------- ordering - def order_by(self, column: str, descending: bool = False) -> QueryBuilder: + def order_by(self, column: str, descending: bool = False) -> Self: """Add sorting order. Can be called multiple times for multi-column sorting. @@ -275,7 +273,7 @@ def order_by(self, column: str, descending: bool = False) -> QueryBuilder: # --------------------------------------------------------------- pagination - def top(self, count: int) -> QueryBuilder: + def top(self, count: int) -> Self: """Limit the total number of results. :param count: Maximum number of records to return (must be >= 1). @@ -287,7 +285,7 @@ def top(self, count: int) -> QueryBuilder: self._top = count return self - def page_size(self, size: int) -> QueryBuilder: + def page_size(self, size: int) -> Self: """Set the number of records per page. Controls how many records are returned in each page/batch @@ -302,7 +300,7 @@ def page_size(self, size: int) -> QueryBuilder: self._page_size = size return self - def count(self) -> QueryBuilder: + def count(self) -> Self: """Request a count of matching records in the response. Adds ``$count=true`` to the query, causing the server to include @@ -321,7 +319,7 @@ def count(self) -> QueryBuilder: self._count = True return self - def include_formatted_values(self) -> QueryBuilder: + def include_formatted_values(self) -> Self: """Request formatted values in the response. Adds ``Prefer: odata.include-annotations="OData.Community.Display.V1.FormattedValue"`` @@ -351,7 +349,7 @@ def include_formatted_values(self) -> QueryBuilder: self._include_annotations = "OData.Community.Display.V1.FormattedValue" return self - def include_annotations(self, annotation: str = "*") -> QueryBuilder: + def include_annotations(self, annotation: str = "*") -> Self: """Request specific OData annotations in the response. Sets the ``Prefer: odata.include-annotations`` header. Use ``"*"`` @@ -379,7 +377,7 @@ def include_annotations(self, annotation: str = "*") -> QueryBuilder: # --------------------------------------------------------------- expand - def expand(self, *relations: Union[str, ExpandOption]) -> QueryBuilder: + def expand(self, *relations: Union[str, ExpandOption]) -> Self: """Expand navigation properties. Accepts plain navigation property names (case-sensitive, passed @@ -448,6 +446,32 @@ def build(self) -> QueryParams: params["include_annotations"] = self._include_annotations return params + +class QueryBuilder(_QueryBuilderBase): + """Fluent interface for building and executing OData queries against a sync client. + + Provides method chaining for constructing complex queries with + composable filter expressions. Can be used standalone (via :meth:`build`) + or bound to a client (via :meth:`execute`). + + :param table: Table schema name to query. + :type table: str + :raises ValueError: If ``table`` is empty. + + Example: + Standalone query construction:: + + from PowerPlatform.Dataverse.models.filters import col + + query = (QueryBuilder("account") + .select("name") + .where(col("statecode") == 0) + .top(10)) + params = query.build() + # {"table": "account", "select": ["name"], + # "filter": "statecode eq 0", "top": 10} + """ + # --------------------------------------------------------------- execute def execute(self, *, by_page=_BY_PAGE_UNSET) -> Union[QueryResult, Iterator[QueryResult]]: diff --git a/src/PowerPlatform/Dataverse/operations/batch.py b/src/PowerPlatform/Dataverse/operations/batch.py index aa5d8391..062da4ab 100644 --- a/src/PowerPlatform/Dataverse/operations/batch.py +++ b/src/PowerPlatform/Dataverse/operations/batch.py @@ -6,7 +6,7 @@ from __future__ import annotations import warnings -from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Protocol, Union import pandas as pd @@ -59,6 +59,29 @@ ] +# --------------------------------------------------------------------------- +# Shared interface for batch operation namespaces +# --------------------------------------------------------------------------- + + +class _BatchContext(Protocol): + """Structural interface required by batch operation namespaces. + + The operation namespaces (BatchRecordOperations, BatchTableOperations, etc.) + are pure (no I/O) and shared by both the sync and async batch implementations. + This Protocol allows them to type-annotate their ``batch`` parameter correctly + without importing either concrete class (``BatchRequest`` or + ``AsyncBatchRequest``), which would otherwise require ``# type: ignore``. + + Both :class:`~PowerPlatform.Dataverse.operations.batch.BatchRequest` and + :class:`~PowerPlatform.Dataverse.aio.operations.async_batch.AsyncBatchRequest` + satisfy this protocol structurally — no explicit inheritance needed. + """ + + _items: List[Any] + records: Any # used by BatchDataFrameOperations to delegate create/update/delete + + # --------------------------------------------------------------------------- # Changeset namespaces # --------------------------------------------------------------------------- @@ -180,7 +203,7 @@ class BatchRecordOperations: Do not instantiate directly; use ``batch.records``. """ - def __init__(self, batch: "BatchRequest") -> None: + def __init__(self, batch: "_BatchContext") -> None: self._batch = batch def create( @@ -482,7 +505,7 @@ class BatchTableOperations: Do not instantiate directly; use ``batch.tables``. """ - def __init__(self, batch: "BatchRequest") -> None: + def __init__(self, batch: "_BatchContext") -> None: self._batch = batch def create( @@ -720,7 +743,7 @@ class BatchQueryOperations: Do not instantiate directly; use ``batch.query``. """ - def __init__(self, batch: "BatchRequest") -> None: + def __init__(self, batch: "_BatchContext") -> None: self._batch = batch def sql(self, sql: str) -> None: @@ -774,7 +797,7 @@ class BatchDataFrameOperations: result = batch.execute() """ - def __init__(self, batch: "BatchRequest") -> None: + def __init__(self, batch: "_BatchContext") -> None: self._batch = batch def create(self, table: str, records: pd.DataFrame) -> None: diff --git a/tests/unit/data/test_sql_parse.py b/tests/unit/data/test_sql_parse.py index d01ecd00..e95888df 100644 --- a/tests/unit/data/test_sql_parse.py +++ b/tests/unit/data/test_sql_parse.py @@ -513,7 +513,7 @@ def test_extract_pagingcookie_malformed_url_returns_none(): def test_extract_pagingcookie_exception_returns_none(): """Returns None when an unexpected exception is raised during URL parsing (except branch).""" - with patch("PowerPlatform.Dataverse.data._odata.urlparse", side_effect=RuntimeError("boom")): + with patch("PowerPlatform.Dataverse.data._odata_base.urlparse", side_effect=RuntimeError("boom")): assert _extract_pagingcookie("https://org.example/?$skiptoken=x") is None From a4f69cd31aa602f9fcfca980b3fb1692e618b11c Mon Sep 17 00:00:00 2001 From: Abel Milash Date: Mon, 18 May 2026 10:11:45 -0700 Subject: [PATCH 02/34] Rebase async implementation on main (post GA API merge) - Reset to main via refactoring branch - Restore full async implementation: aio/ client, HTTP, batch, OData, relationships, upload, query builder, fetchxml, operations, tests, examples - Re-export multipart helpers from _batch.py for test compatibility - All 2166 tests passing Co-Authored-By: Claude Sonnet 4.6 --- .azdo/ci-pr.yaml | 2 +- .github/workflows/python-package.yml | 2 +- examples/aio/__init__.py | 2 + examples/aio/_auth.py | 57 + examples/aio/advanced/__init__.py | 2 + .../aio/advanced/alternate_keys_upsert.py | 275 +++ examples/aio/advanced/batch.py | 280 +++ examples/aio/advanced/dataframe_operations.py | 191 ++ .../advanced/datascience_risk_assessment.py | 665 ++++++ examples/aio/advanced/fetchxml.py | 574 +++++ examples/aio/advanced/file_upload.py | 361 ++++ examples/aio/advanced/prodev_quick_start.py | 475 +++++ examples/aio/advanced/relationships.py | 401 ++++ examples/aio/advanced/sql_examples.py | 925 +++++++++ examples/aio/advanced/walkthrough.py | 632 ++++++ examples/aio/basic/__init__.py | 2 + examples/aio/basic/functional_testing.py | 898 ++++++++ examples/aio/basic/installation_example.py | 372 ++++ pyproject.toml | 24 +- src/PowerPlatform/Dataverse/aio/__init__.py | 12 + .../Dataverse/aio/async_client.py | 252 +++ .../Dataverse/aio/core/__init__.py | 9 + .../Dataverse/aio/core/_async_auth.py | 45 + .../Dataverse/aio/core/_async_http.py | 185 ++ .../Dataverse/aio/data/__init__.py | 9 + .../Dataverse/aio/data/_async_batch.py | 295 +++ .../Dataverse/aio/data/_async_odata.py | 1835 ++++++++++++++++ .../aio/data/_async_relationships.py | 263 +++ .../Dataverse/aio/data/_async_upload.py | 193 ++ .../Dataverse/aio/models/__init__.py | 13 + .../aio/models/async_fetchxml_query.py | 158 ++ .../aio/models/async_query_builder.py | 141 ++ .../Dataverse/aio/operations/__init__.py | 13 + .../Dataverse/aio/operations/async_batch.py | 174 ++ .../aio/operations/async_dataframe.py | 309 +++ .../Dataverse/aio/operations/async_files.py | 113 + .../Dataverse/aio/operations/async_query.py | 371 ++++ .../Dataverse/aio/operations/async_records.py | 522 +++++ .../Dataverse/aio/operations/async_tables.py | 838 ++++++++ src/PowerPlatform/Dataverse/client.py | 2 +- src/PowerPlatform/Dataverse/models/record.py | 4 +- .../Dataverse/operations/query.py | 16 +- tests/unit/aio/__init__.py | 2 + tests/unit/aio/conftest.py | 35 + tests/unit/aio/core/__init__.py | 2 + tests/unit/aio/core/test_async_auth.py | 49 + tests/unit/aio/core/test_async_http.py | 286 +++ tests/unit/aio/data/__init__.py | 0 .../aio/data/test_async_batch_internal.py | 839 ++++++++ .../aio/data/test_async_odata_internal.py | 1846 +++++++++++++++++ .../unit/aio/data/test_async_relationships.py | 314 +++ tests/unit/aio/data/test_async_upload.py | 318 +++ tests/unit/aio/test_async_batch.py | 511 +++++ tests/unit/aio/test_async_client.py | 241 +++ tests/unit/aio/test_async_dataframe.py | 183 ++ tests/unit/aio/test_async_files.py | 53 + tests/unit/aio/test_async_query.py | 542 +++++ tests/unit/aio/test_async_records.py | 541 +++++ tests/unit/aio/test_async_tables.py | 314 +++ tests/unit/core/test_http_errors.py | 2 - tests/unit/models/test_query_builder.py | 1 - tests/unit/test_phase1_ga.py | 2 - tests/unit/test_phase3_ga.py | 2 +- 63 files changed, 17963 insertions(+), 32 deletions(-) create mode 100644 examples/aio/__init__.py create mode 100644 examples/aio/_auth.py create mode 100644 examples/aio/advanced/__init__.py create mode 100644 examples/aio/advanced/alternate_keys_upsert.py create mode 100644 examples/aio/advanced/batch.py create mode 100644 examples/aio/advanced/dataframe_operations.py create mode 100644 examples/aio/advanced/datascience_risk_assessment.py create mode 100644 examples/aio/advanced/fetchxml.py create mode 100644 examples/aio/advanced/file_upload.py create mode 100644 examples/aio/advanced/prodev_quick_start.py create mode 100644 examples/aio/advanced/relationships.py create mode 100644 examples/aio/advanced/sql_examples.py create mode 100644 examples/aio/advanced/walkthrough.py create mode 100644 examples/aio/basic/__init__.py create mode 100644 examples/aio/basic/functional_testing.py create mode 100644 examples/aio/basic/installation_example.py create mode 100644 src/PowerPlatform/Dataverse/aio/__init__.py create mode 100644 src/PowerPlatform/Dataverse/aio/async_client.py create mode 100644 src/PowerPlatform/Dataverse/aio/core/__init__.py create mode 100644 src/PowerPlatform/Dataverse/aio/core/_async_auth.py create mode 100644 src/PowerPlatform/Dataverse/aio/core/_async_http.py create mode 100644 src/PowerPlatform/Dataverse/aio/data/__init__.py create mode 100644 src/PowerPlatform/Dataverse/aio/data/_async_batch.py create mode 100644 src/PowerPlatform/Dataverse/aio/data/_async_odata.py create mode 100644 src/PowerPlatform/Dataverse/aio/data/_async_relationships.py create mode 100644 src/PowerPlatform/Dataverse/aio/data/_async_upload.py create mode 100644 src/PowerPlatform/Dataverse/aio/models/__init__.py create mode 100644 src/PowerPlatform/Dataverse/aio/models/async_fetchxml_query.py create mode 100644 src/PowerPlatform/Dataverse/aio/models/async_query_builder.py create mode 100644 src/PowerPlatform/Dataverse/aio/operations/__init__.py create mode 100644 src/PowerPlatform/Dataverse/aio/operations/async_batch.py create mode 100644 src/PowerPlatform/Dataverse/aio/operations/async_dataframe.py create mode 100644 src/PowerPlatform/Dataverse/aio/operations/async_files.py create mode 100644 src/PowerPlatform/Dataverse/aio/operations/async_query.py create mode 100644 src/PowerPlatform/Dataverse/aio/operations/async_records.py create mode 100644 src/PowerPlatform/Dataverse/aio/operations/async_tables.py create mode 100644 tests/unit/aio/__init__.py create mode 100644 tests/unit/aio/conftest.py create mode 100644 tests/unit/aio/core/__init__.py create mode 100644 tests/unit/aio/core/test_async_auth.py create mode 100644 tests/unit/aio/core/test_async_http.py create mode 100644 tests/unit/aio/data/__init__.py create mode 100644 tests/unit/aio/data/test_async_batch_internal.py create mode 100644 tests/unit/aio/data/test_async_odata_internal.py create mode 100644 tests/unit/aio/data/test_async_relationships.py create mode 100644 tests/unit/aio/data/test_async_upload.py create mode 100644 tests/unit/aio/test_async_batch.py create mode 100644 tests/unit/aio/test_async_client.py create mode 100644 tests/unit/aio/test_async_dataframe.py create mode 100644 tests/unit/aio/test_async_files.py create mode 100644 tests/unit/aio/test_async_query.py create mode 100644 tests/unit/aio/test_async_records.py create mode 100644 tests/unit/aio/test_async_tables.py diff --git a/.azdo/ci-pr.yaml b/.azdo/ci-pr.yaml index 80fc4b4d..178875b0 100644 --- a/.azdo/ci-pr.yaml +++ b/.azdo/ci-pr.yaml @@ -43,7 +43,7 @@ extends: - script: | python -m pip install --upgrade pip python -m pip install flake8 black build diff-cover - python -m pip install -e .[dev] + python -m pip install -e .[dev,async] displayName: 'Install dependencies' - script: | diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 886bc72b..0178b090 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -30,7 +30,7 @@ jobs: run: | python -m pip install --upgrade pip python -m pip install flake8 black build diff-cover - python -m pip install -e .[dev] + python -m pip install -e .[dev,async] - name: Check format with black run: | diff --git a/examples/aio/__init__.py b/examples/aio/__init__.py new file mode 100644 index 00000000..9a045456 --- /dev/null +++ b/examples/aio/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. diff --git a/examples/aio/_auth.py b/examples/aio/_auth.py new file mode 100644 index 00000000..a425321d --- /dev/null +++ b/examples/aio/_auth.py @@ -0,0 +1,57 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +""" +Async credential helper for the async example scripts. + +azure-identity's InteractiveBrowserCredential is only available in the sync +namespace (azure.identity), not the async one (azure.identity.aio). This +module wraps the sync credential so it satisfies the AsyncTokenCredential +protocol required by AsyncDataverseClient. + +Usage:: + + from _auth import AsyncInteractiveBrowserCredential + + credential = AsyncInteractiveBrowserCredential() + try: + async with AsyncDataverseClient(org_url, credential) as client: + ... + finally: + await credential.close() +""" + +import asyncio +from concurrent.futures import ThreadPoolExecutor + +from azure.identity import InteractiveBrowserCredential + + +class AsyncInteractiveBrowserCredential: + """ + Async wrapper around the sync InteractiveBrowserCredential. + + get_token() is dispatched to a dedicated thread so the event loop stays + free during the browser popup / token exchange. Subsequent calls hit the + in-process token cache and return almost immediately. + """ + + def __init__(self, **kwargs): + self._credential = InteractiveBrowserCredential(**kwargs) + self._executor = ThreadPoolExecutor(max_workers=1) + + async def get_token(self, *scopes, **kwargs): + loop = asyncio.get_running_loop() + return await loop.run_in_executor( + self._executor, + lambda: self._credential.get_token(*scopes, **kwargs), + ) + + async def close(self): + self._executor.shutdown(wait=False) + + async def __aenter__(self): + return self + + async def __aexit__(self, *_): + await self.close() diff --git a/examples/aio/advanced/__init__.py b/examples/aio/advanced/__init__.py new file mode 100644 index 00000000..9a045456 --- /dev/null +++ b/examples/aio/advanced/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. diff --git a/examples/aio/advanced/alternate_keys_upsert.py b/examples/aio/advanced/alternate_keys_upsert.py new file mode 100644 index 00000000..a080975d --- /dev/null +++ b/examples/aio/advanced/alternate_keys_upsert.py @@ -0,0 +1,275 @@ +#!/usr/bin/env python3 +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +""" +PowerPlatform Dataverse Client - Async Alternate Keys & Upsert Example + +Async equivalent of examples/advanced/alternate_keys_upsert.py. + +Demonstrates the full workflow of creating alternate keys and using +them for upsert operations: +1. Create a custom table with columns +2. Define an alternate key on a column +3. Wait for the key index to become Active +4. Upsert records using the alternate key +5. Verify records were created/updated correctly +6. Clean up + +Prerequisites: + pip install PowerPlatform-Dataverse-Client + pip install azure-identity +""" + +import asyncio +import sys + +from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient +from PowerPlatform.Dataverse.models.upsert import UpsertItem +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parents[1])) +from _auth import AsyncInteractiveBrowserCredential + +# --- Config --- +TABLE_NAME = "new_AltKeyDemo" +KEY_COLUMN = "new_externalid" +KEY_NAME = "new_ExternalIdKey" +BACKOFF_DELAYS = (0, 3, 10, 20, 35) + + +# --- Helpers --- +async def backoff(coro_fn, *, delays=BACKOFF_DELAYS): + """Retry *coro_fn* with exponential-ish backoff on any exception.""" + last = None + total_delay = 0 + attempts = 0 + for d in delays: + if d: + await asyncio.sleep(d) + total_delay += d + attempts += 1 + try: + result = await coro_fn() + if attempts > 1: + retry_count = attempts - 1 + print(f" [INFO] Backoff succeeded after {retry_count} retry(s); " f"waited {total_delay}s total.") + return result + except Exception as ex: # noqa: BLE001 + last = ex + continue + if last: + if attempts: + retry_count = max(attempts - 1, 0) + print(f" [WARN] Backoff exhausted after {retry_count} retry(s); " f"waited {total_delay}s total.") + raise last + + +async def wait_for_key_active(client, table, key_name, max_wait=120): + """Poll get_alternate_keys until the key status is Active.""" + import time + + start = time.time() + while time.time() - start < max_wait: + keys = await client.tables.get_alternate_keys(table) + for k in keys: + if k.schema_name == key_name: + print(f" Key status: {k.status}") + if k.status == "Active": + return k + if k.status == "Failed": + raise RuntimeError(f"Alternate key index failed: {k.schema_name}") + await asyncio.sleep(5) + raise TimeoutError(f"Key {key_name} did not become Active within {max_wait}s") + + +# --- Main --- +async def main(): + """Run the async alternate-keys & upsert E2E walkthrough.""" + print("PowerPlatform Dataverse Client - Async Alternate Keys & Upsert Example") + print("=" * 70) + print("This script demonstrates:") + print(" - Creating a custom table with columns") + print(" - Defining an alternate key on a column") + print(" - Waiting for the key index to become Active") + print(" - Upserting records via alternate key (create + update)") + print(" - Verifying records and listing keys") + print(" - Cleaning up (delete key, delete table)") + print("=" * 70) + + entered = input("Enter Dataverse org URL (e.g. https://yourorg.crm.dynamics.com): ").strip() + if not entered: + print("No URL entered; exiting.") + sys.exit(1) + + base_url = entered.rstrip("/") + credential = AsyncInteractiveBrowserCredential() + try: + async with AsyncDataverseClient(base_url, credential) as client: + + # ------------------------------------------------------------------ + # Step 1: Create table (skip if already exists) + # ------------------------------------------------------------------ + print("\n1. Creating table...") + table_info = await client.tables.get(TABLE_NAME) + if table_info: + print(f" Table already exists: {TABLE_NAME} (skipped)") + else: + table_info = await backoff( + lambda: client.tables.create( + TABLE_NAME, + columns={ + KEY_COLUMN: "string", + "new_ProductName": "string", + "new_Price": "decimal", + }, + ) + ) + print(f" Created: {table_info.get('table_schema_name', TABLE_NAME)}") + await asyncio.sleep(10) # Wait for metadata propagation + + # ------------------------------------------------------------------ + # Step 2: Create alternate key (skip if already exists) + # ------------------------------------------------------------------ + print("\n2. Creating alternate key...") + existing_keys = await client.tables.get_alternate_keys(TABLE_NAME) + existing_key = next((k for k in existing_keys if k.schema_name == KEY_NAME), None) + if existing_key: + print(f" Alternate key already exists: {KEY_NAME} (skipped)") + else: + key_info = await backoff( + lambda: client.tables.create_alternate_key(TABLE_NAME, KEY_NAME, [KEY_COLUMN.lower()]) + ) + print(f" Key created: {key_info.schema_name} (id={key_info.metadata_id})") + + # ------------------------------------------------------------------ + # Step 3: Wait for key to become Active + # ------------------------------------------------------------------ + print("\n3. Waiting for key index to become Active...") + active_key = await wait_for_key_active(client, TABLE_NAME, KEY_NAME) + print(f" Key is Active: {active_key.schema_name}") + + # ------------------------------------------------------------------ + # Step 4: Upsert records (creates new) + # ------------------------------------------------------------------ + print("\n4a. Upsert single record (PATCH, creates new)...") + await client.records.upsert( + TABLE_NAME, + [ + UpsertItem( + alternate_key={KEY_COLUMN.lower(): "EXT-001"}, + record={"new_productname": "Widget A", "new_price": 9.99}, + ), + ], + ) + print(" Upserted EXT-001 (single)") + + print("\n4b. Upsert second record (single PATCH)...") + await client.records.upsert( + TABLE_NAME, + [ + UpsertItem( + alternate_key={KEY_COLUMN.lower(): "EXT-002"}, + record={"new_productname": "Widget B", "new_price": 19.99}, + ), + ], + ) + print(" Upserted EXT-002 (single)") + + print("\n4c. Upsert multiple records (UpsertMultiple bulk)...") + await client.records.upsert( + TABLE_NAME, + [ + UpsertItem( + alternate_key={KEY_COLUMN.lower(): "EXT-003"}, + record={"new_productname": "Widget C", "new_price": 29.99}, + ), + UpsertItem( + alternate_key={KEY_COLUMN.lower(): "EXT-004"}, + record={"new_productname": "Widget D", "new_price": 39.99}, + ), + ], + ) + print(" Upserted EXT-003, EXT-004 (bulk)") + + # ------------------------------------------------------------------ + # Step 5a: Upsert single update (PATCH, record exists) + # ------------------------------------------------------------------ + print("\n5a. Upsert single record (update existing via PATCH)...") + await client.records.upsert( + TABLE_NAME, + [ + UpsertItem( + alternate_key={KEY_COLUMN.lower(): "EXT-001"}, + record={"new_productname": "Widget A v2", "new_price": 12.99}, + ), + ], + ) + print(" Updated EXT-001 (single)") + + # ------------------------------------------------------------------ + # Step 5b: Upsert multiple update (UpsertMultiple, records exist) + # ------------------------------------------------------------------ + print("\n5b. Upsert multiple records (update existing via UpsertMultiple)...") + await client.records.upsert( + TABLE_NAME, + [ + UpsertItem( + alternate_key={KEY_COLUMN.lower(): "EXT-003"}, + record={"new_productname": "Widget C v2", "new_price": 31.99}, + ), + UpsertItem( + alternate_key={KEY_COLUMN.lower(): "EXT-004"}, + record={"new_productname": "Widget D v2", "new_price": 41.99}, + ), + ], + ) + print(" Updated EXT-003, EXT-004 (bulk)") + + # ------------------------------------------------------------------ + # Step 6: Verify + # ------------------------------------------------------------------ + print("\n6. Verifying records...") + async for record in client.records.list_pages( + TABLE_NAME, + select=["new_productname", "new_price", KEY_COLUMN.lower()], + ): + for item in record: + ext_id = item.get(KEY_COLUMN.lower(), "?") + name = item.get("new_productname", "?") + price = item.get("new_price", "?") + print(f" {ext_id}: {name} @ ${price}") + + # ------------------------------------------------------------------ + # Step 7: List alternate keys + # ------------------------------------------------------------------ + print("\n7. Listing alternate keys...") + keys = await client.tables.get_alternate_keys(TABLE_NAME) + for k in keys: + print(f" {k.schema_name}: columns={k.key_attributes}, status={k.status}") + + # ------------------------------------------------------------------ + # Step 8: Cleanup + # ------------------------------------------------------------------ + cleanup = input("\n8. Delete table and cleanup? (Y/n): ").strip() or "y" + if cleanup.lower() in ("y", "yes"): + try: + # Delete alternate key first + for k in keys: + await client.tables.delete_alternate_key(TABLE_NAME, k.metadata_id) + print(f" Deleted key: {k.schema_name}") + await asyncio.sleep(5) + await backoff(lambda: client.tables.delete(TABLE_NAME)) + print(f" Deleted table: {TABLE_NAME}") + except Exception as e: # noqa: BLE001 + print(f" Cleanup error: {e}") + else: + print(" Table kept for inspection.") + finally: + await credential.close() + + print("\nDone.") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/aio/advanced/batch.py b/examples/aio/advanced/batch.py new file mode 100644 index 00000000..7023a85b --- /dev/null +++ b/examples/aio/advanced/batch.py @@ -0,0 +1,280 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +""" +Async batch operations example for the Dataverse Python SDK. + +Async equivalent of examples/advanced/batch.py. + +Demonstrates how to use client.batch to send multiple operations in a single +HTTP request to the Dataverse Web API using the async client. + +Requirements: + pip install PowerPlatform-Dataverse-Client azure-identity +""" + +from __future__ import annotations + +import asyncio +import sys + +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parents[1])) +from _auth import AsyncInteractiveBrowserCredential +from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient + + +async def main(): + base_url = input("Enter Dataverse org URL (e.g. https://yourorg.crm.dynamics.com): ").strip() + if not base_url: + print("No URL entered; exiting.") + sys.exit(1) + base_url = base_url.rstrip("/") + + credential = AsyncInteractiveBrowserCredential() + try: + async with AsyncDataverseClient(base_url=base_url, credential=credential) as client: + + # --------------------------------------------------------------------------- + # Example 1: Record CRUD in a single batch + # --------------------------------------------------------------------------- + + print("\n[INFO] Example 1: Record CRUD in a single batch") + + batch = client.batch.new() + + # Create a single record + batch.records.create("account", {"name": "Contoso Ltd", "telephone1": "555-0100"}) + + # Create multiple records via CreateMultiple (one batch item) + batch.records.create( + "contact", + [ + {"firstname": "Alice", "lastname": "Smith"}, + {"firstname": "Bob", "lastname": "Jones"}, + ], + ) + + # Assume we have an existing account_id from a prior operation + # batch.records.update("account", account_id, {"telephone1": "555-9999"}) + # batch.records.delete("account", old_id) + + result = await batch.execute() + + print( + f"[OK] Total: {len(result.responses)}, Succeeded: {len(result.succeeded)}, Failed: {len(result.failed)}" + ) + for guid in result.entity_ids: + print(f"[OK] Created: {guid}") + for item in result.failed: + print(f"[ERR] {item.status_code}: {item.error_message}") + + # --------------------------------------------------------------------------- + # Example 2: Transactional changeset with content-ID chaining + # --------------------------------------------------------------------------- + + print("\n[INFO] Example 2: Transactional changeset") + + batch = client.batch.new() + + async with batch.changeset() as cs: + # Each create() returns a "$n" reference usable in subsequent operations + lead_ref = cs.records.create( + "lead", + {"firstname": "Ada", "lastname": "Lovelace"}, + ) + contact_ref = cs.records.create("contact", {"firstname": "Ada"}) + + # Reference the newly created lead and contact in the account + cs.records.create( + "account", + { + "name": "Babbage & Co.", + "originatingleadid@odata.bind": lead_ref, + "primarycontactid@odata.bind": contact_ref, + }, + ) + + # Update using a content-ID reference as the record_id + cs.records.update("contact", contact_ref, {"lastname": "Lovelace"}) + + result = await batch.execute() + + if result.has_errors: + print("[ERR] Changeset rolled back") + for item in result.failed: + print(f" {item.status_code}: {item.error_message}") + else: + print(f"[OK] {len(result.entity_ids)} records created atomically") + + # --------------------------------------------------------------------------- + # Example 3: Table metadata operations in a batch + # --------------------------------------------------------------------------- + + print("\n[INFO] Example 3: Table metadata operations") + + batch = client.batch.new() + + # Create a new custom table + batch.tables.create( + "new_Product", + {"new_Price": "decimal", "new_InStock": "bool"}, + solution="MySolution", + ) + + # Read table metadata + batch.tables.get("new_Product") + + # List all non-private tables + batch.tables.list() + + result = await batch.execute() + print(f"[OK] Table ops: {[(r.status_code, r.is_success) for r in result.responses]}") + + # --------------------------------------------------------------------------- + # Example 4: SQL query in a batch + # --------------------------------------------------------------------------- + + print("\n[INFO] Example 4: SQL query in batch") + + batch = client.batch.new() + batch.query.sql("SELECT TOP 5 accountid, name FROM account ORDER BY name") + + result = await batch.execute() + if result.responses and result.responses[0].is_success and result.responses[0].data: + rows = result.responses[0].data.get("value", []) + print(f"[OK] Retrieved {len(rows)} accounts") + for row in rows: + print(f" {row.get('name')}") + + # --------------------------------------------------------------------------- + # Example 5: Mixed batch — changeset writes + standalone GETs + # --------------------------------------------------------------------------- + + print("\n[INFO] Example 5: Mixed batch") + + # NOTE: Commented out because it requires a pre-existing account_id. + # Uncomment and set account_id to run this example. + # batch = client.batch.new() + # + # async with batch.changeset() as cs: + # cs.records.update("account", account_id, {"statecode": 0}) + # + # batch.records.retrieve("account", account_id, select=["name", "statecode"]) + # + # result = await batch.execute() + # update_response = result.responses[0] + # account_data = result.responses[1] + # if account_data.is_success and account_data.data: + # print(f"Account: {account_data.data.get('name')}") + + # --------------------------------------------------------------------------- + # Example 6: Continue on error + # --------------------------------------------------------------------------- + + print("\n[INFO] Example 6: Continue on error") + + batch = client.batch.new() + batch.records.retrieve("account", "00000000-0000-0000-0000-000000000000") + batch.query.sql("SELECT TOP 1 name FROM account") + + result = await batch.execute(continue_on_error=True) + print(f"[OK] Succeeded: {len(result.succeeded)}, Failed: {len(result.failed)}") + for item in result.failed: + print(f"[ERR] {item.status_code}: {item.error_message}") + + # --------------------------------------------------------------------------- + # Example 7: DataFrame integration + # --------------------------------------------------------------------------- + + print("\n[INFO] Example 7: DataFrame batch operations") + + import pandas as pd + + # Create records from a DataFrame + df = pd.DataFrame( + [ + {"name": "DF-Batch-A", "telephone1": "555-0100"}, + {"name": "DF-Batch-B", "telephone1": "555-0200"}, + ] + ) + batch = client.batch.new() + batch.dataframe.create("account", df) + result = await batch.execute() + print(f"[OK] DataFrame create: {len(result.succeeded)} succeeded") + created_ids = list(result.entity_ids) + + # Update records from a DataFrame + if len(created_ids) >= 2: + update_df = pd.DataFrame( + [ + {"accountid": created_ids[0], "telephone1": "555-9990"}, + {"accountid": created_ids[1], "telephone1": "555-9991"}, + ] + ) + batch = client.batch.new() + batch.dataframe.update("account", update_df, id_column="accountid") + result = await batch.execute() + print(f"[OK] DataFrame update: {len(result.succeeded)} succeeded") + + # Delete records from a Series + if created_ids: + batch = client.batch.new() + batch.dataframe.delete("account", pd.Series(created_ids), use_bulk_delete=False) + result = await batch.execute() + print(f"[OK] DataFrame delete: {len(result.succeeded)} succeeded") + + # --------------------------------------------------------------------------- + # Example 8: Understanding response data patterns + # --------------------------------------------------------------------------- + + print("\n[INFO] Example 8: Response data patterns") + + # Every batch result maps 1:1 with the operations you added. + # Different operations return different response shapes: + + batch = client.batch.new() + # Op 0: single create -> 204 No Content, entity_id in OData-EntityId header + batch.records.create("account", {"name": "Pattern-Demo"}) + # Op 1: bulk create -> 200 OK, IDs in body as {"Ids": [...]} + batch.records.create("account", [{"name": "Bulk-A"}, {"name": "Bulk-B"}]) + # Op 2: SQL query -> 200 OK, rows in body as {"value": [...]} + batch.query.sql("SELECT TOP 3 name FROM account") + + result = await batch.execute() + + for i, resp in enumerate(result.responses): + if not resp.is_success: + print(f" Op {i}: [FAIL] {resp.status_code}: {resp.error_message}") + continue + + # Single create: entity_id from OData-EntityId header + if resp.entity_id: + print(f" Op {i}: [CREATE] entity_id={resp.entity_id}") + + # Bulk action (CreateMultiple/UpsertMultiple): IDs in body + elif resp.data and "Ids" in resp.data: + print(f" Op {i}: [BULK] {len(resp.data['Ids'])} IDs: {resp.data['Ids']}") + + # Query: rows in body + elif resp.data and "value" in resp.data: + print(f" Op {i}: [QUERY] {len(resp.data['value'])} rows") + + # Delete or metadata operation: 204, no data + else: + print(f" Op {i}: [OK] {resp.status_code}") + + # Clean up demo records + for rid in result.entity_ids: + await client.records.delete("account", rid) + for resp in result.succeeded: + if resp.data and "Ids" in resp.data: + for rid in resp.data["Ids"]: + await client.records.delete("account", rid) + finally: + await credential.close() + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/aio/advanced/dataframe_operations.py b/examples/aio/advanced/dataframe_operations.py new file mode 100644 index 00000000..463f38fe --- /dev/null +++ b/examples/aio/advanced/dataframe_operations.py @@ -0,0 +1,191 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +""" +PowerPlatform Dataverse Client - Async DataFrame Operations Walkthrough + +Async equivalent of examples/advanced/dataframe_operations.py. + +This example demonstrates how to use the async pandas DataFrame extension +methods for CRUD operations with Microsoft Dataverse. + +Prerequisites: + pip install PowerPlatform-Dataverse-Client + pip install azure-identity +""" + +import asyncio +import sys +import uuid + +import pandas as pd +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parents[1])) +from _auth import AsyncInteractiveBrowserCredential + +from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient +from PowerPlatform.Dataverse.models.filters import col, raw + + +async def main(): + # -- Setup & Authentication ------------------------------------ + base_url = input("Enter Dataverse org URL (e.g. https://yourorg.crm.dynamics.com): ").strip() + if not base_url: + print("[ERR] No URL entered; exiting.") + sys.exit(1) + base_url = base_url.rstrip("/") + + print("[INFO] Authenticating via browser...") + credential = AsyncInteractiveBrowserCredential() + try: + async with AsyncDataverseClient(base_url, credential) as client: + await _run_walkthrough(client) + finally: + await credential.close() + + +async def _run_walkthrough(client): + table = input("Enter table schema name to use [default: account]: ").strip() or "account" + print(f"[INFO] Using table: {table}") + + # Unique tag to isolate test records from existing data + tag = uuid.uuid4().hex[:8] + test_filter = f"contains(name,'{tag}')" + print(f"[INFO] Using tag '{tag}' to identify test records") + + select_cols = ["name", "telephone1", "websiteurl", "lastonholdtime"] + + # -- 1. Create records from a DataFrame ------------------------ + print("\n" + "-" * 60) + print("1. Create records from a DataFrame") + print("-" * 60) + + new_accounts = pd.DataFrame( + [ + { + "name": f"Contoso_{tag}", + "telephone1": "555-0100", + "websiteurl": "https://contoso.com", + "lastonholdtime": pd.Timestamp("2024-06-15 10:30:00"), + }, + {"name": f"Fabrikam_{tag}", "telephone1": "555-0200", "websiteurl": None, "lastonholdtime": None}, + { + "name": f"Northwind_{tag}", + "telephone1": None, + "websiteurl": "https://northwind.com", + "lastonholdtime": pd.Timestamp("2024-12-01 08:00:00"), + }, + ] + ) + print(f" Input DataFrame:\n{new_accounts.to_string(index=False)}\n") + + # create returns a Series of GUIDs aligned with the input rows + new_accounts["accountid"] = await client.dataframe.create(table, new_accounts) + print(f"[OK] Created {len(new_accounts)} records") + print(f" IDs: {new_accounts['accountid'].tolist()}") + + # -- 2. Query records as a DataFrame ------------------------- + print("\n" + "-" * 60) + print("2. Query records as a DataFrame") + print("-" * 60) + + result = await client.query.builder(table).select(*select_cols).where(raw(test_filter)).execute() + df_all = result.to_dataframe() + print(f"[OK] Got {len(df_all)} records in one DataFrame") + print(f" Columns: {list(df_all.columns)}") + print(f"{df_all.to_string(index=False)}") + + # -- 3. Limit results with top ------------------------------ + print("\n" + "-" * 60) + print("3. Limit results with top") + print("-" * 60) + + result_top2 = await client.query.builder(table).select(*select_cols).where(raw(test_filter)).top(2).execute() + df_top2 = result_top2.to_dataframe() + print(f"[OK] Got {len(df_top2)} records with top=2") + print(f"{df_top2.to_string(index=False)}") + + # -- 4. Fetch a single record by ID ---------------------------- + print("\n" + "-" * 60) + print("4. Fetch a single record by ID") + print("-" * 60) + + first_id = new_accounts["accountid"].iloc[0] + print(f" Fetching record {first_id}...") + result_single = await client.query.builder(table).select(*select_cols).where(col("accountid") == first_id).execute() + single = result_single.to_dataframe() + print(f"[OK] Single record DataFrame:\n{single.to_string(index=False)}") + + # -- 5. Update records with different values per row ----------- + print("\n" + "-" * 60) + print("5. Update records with different values per row") + print("-" * 60) + + new_accounts["telephone1"] = ["555-1100", "555-1200", "555-1300"] + print(f" New telephone numbers: {new_accounts['telephone1'].tolist()}") + await client.dataframe.update(table, new_accounts[["accountid", "telephone1"]], id_column="accountid") + print("[OK] Updated 3 records") + + # Verify the updates + result_verified = await client.query.builder(table).select(*select_cols).where(raw(test_filter)).execute() + verified = result_verified.to_dataframe() + print(f" Verified:\n{verified.to_string(index=False)}") + + # -- 6. Broadcast update (same value to all records) ----------- + print("\n" + "-" * 60) + print("6. Broadcast update (same value to all records)") + print("-" * 60) + + broadcast_df = new_accounts[["accountid"]].copy() + broadcast_df["websiteurl"] = "https://updated.example.com" + print(f" Setting websiteurl to 'https://updated.example.com' for all {len(broadcast_df)} records") + await client.dataframe.update(table, broadcast_df, id_column="accountid") + print("[OK] Broadcast update complete") + + # Verify all records have the same websiteurl + result_bc = await client.query.builder(table).select(*select_cols).where(raw(test_filter)).execute() + print(f" Verified:\n{result_bc.to_dataframe().to_string(index=False)}") + + # Default: NaN/None fields are skipped (not overridden on server) + print("\n Updating with NaN values (default: clear_nulls=False, fields should stay unchanged)...") + sparse_df = pd.DataFrame( + [ + {"accountid": new_accounts["accountid"].iloc[0], "telephone1": "555-9999", "websiteurl": None}, + ] + ) + await client.dataframe.update(table, sparse_df, id_column="accountid") + result_sparse = await client.query.builder(table).select(*select_cols).where(raw(test_filter)).execute() + print( + f" Verified (Contoso telephone1 updated, websiteurl unchanged):\n" + f"{result_sparse.to_dataframe().to_string(index=False)}" + ) + + # Opt-in: clear_nulls=True sends None as null to clear the field + print("\n Clearing websiteurl for Contoso with clear_nulls=True...") + clear_df = pd.DataFrame([{"accountid": new_accounts["accountid"].iloc[0], "websiteurl": None}]) + await client.dataframe.update(table, clear_df, id_column="accountid", clear_nulls=True) + result_clear = await client.query.builder(table).select(*select_cols).where(raw(test_filter)).execute() + print(f" Verified (Contoso websiteurl should be empty):\n" f"{result_clear.to_dataframe().to_string(index=False)}") + + # -- 7. Delete records by passing a Series of GUIDs ------------ + print("\n" + "-" * 60) + print("7. Delete records by passing a Series of GUIDs") + print("-" * 60) + + print(f" Deleting {len(new_accounts)} records...") + await client.dataframe.delete(table, new_accounts["accountid"], use_bulk_delete=False) + print(f"[OK] Deleted {len(new_accounts)} records") + + # Verify deletions -- filter for our tagged records should return 0 + result_remaining = await client.query.builder(table).select(*select_cols).where(raw(test_filter)).execute() + remaining = result_remaining.to_dataframe() + print(f" Verified: {len(remaining)} test records remaining (expected 0)") + + print("\n" + "=" * 60) + print("[OK] Async DataFrame operations walkthrough complete!") + print("=" * 60) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/aio/advanced/datascience_risk_assessment.py b/examples/aio/advanced/datascience_risk_assessment.py new file mode 100644 index 00000000..8d88fd68 --- /dev/null +++ b/examples/aio/advanced/datascience_risk_assessment.py @@ -0,0 +1,665 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +""" +PowerPlatform Dataverse Client - Async Data Science Risk Assessment Pipeline + +Async equivalent of examples/advanced/datascience_risk_assessment.py. + +End-to-end example: Extract Dataverse data concurrently into DataFrames, +run statistical analysis, generate LLM-powered risk summaries, and write +results back to Dataverse -- a realistic data analyst / data scientist workflow. + +Pipeline flow: + Dataverse SDK (async) --> Pandas DataFrame --> Analysis + LLM --> Write-back & Reports + +The three Dataverse extraction queries (accounts, cases, opportunities) run +concurrently via asyncio.gather(), reducing wall-clock time for the extract step. + +Scenario: + A financial services company tracks customer accounts, service cases, and + revenue opportunities in Dataverse. The risk team needs to: + 1) Pull data from multiple tables into DataFrames (concurrently) + 2) Compute risk scores using statistical analysis (pandas/numpy) + 3) Classify and summarize risk using an LLM + 4) Write risk assessments back to Dataverse + 5) Produce a summary report + + Note: This example reads from existing Dataverse tables (account, + incident, opportunity) and does not create or delete any tables. + Step 4 (write-back) is disabled by default -- uncomment it in + run_risk_pipeline() to write risk scores back to account records. + +Prerequisites (required -- included in SDK dependencies): + pip install PowerPlatform-Dataverse-Client + pip install azure-identity + +Additional libraries (optional -- used for visualization and LLM): + pip install matplotlib + pip install azure-ai-inference # Option A: Azure AI Foundry / Azure OpenAI + pip install openai # Option B: OpenAI / Azure OpenAI +""" + +import asyncio +import sys +import warnings +from pathlib import Path +from textwrap import dedent + +# Suppress MSAL advisory about response_mode (third-party library, not actionable here) +warnings.filterwarnings("ignore", message="response_mode=.*form_post", category=UserWarning) + +import numpy as np +import pandas as pd + +sys.path.insert(0, str(Path(__file__).resolve().parents[1])) +from _auth import AsyncInteractiveBrowserCredential + +from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient +from PowerPlatform.Dataverse.models.filters import col, raw + +# -- Optional imports (graceful degradation if not installed) ------ + +try: + import matplotlib + + matplotlib.use("Agg") # non-interactive backend (no GUI required) + import matplotlib.pyplot as plt + + HAS_MATPLOTLIB = True +except ImportError: + HAS_MATPLOTLIB = False + + +# ================================================================ +# LLM Provider Configuration +# ================================================================ +# Same providers as the sync version. LLM calls are kept synchronous +# here since they are CPU-light blocking calls. Replace with async +# LLM clients (e.g. openai.AsyncOpenAI) if latency matters. + + +def get_llm_client(provider=None, endpoint=None, api_key=None, model="gpt-4o"): + """Create an LLM client using the specified (or first available) provider. + + Returns a callable: llm_complete(system_prompt, user_prompt) -> str + Returns None if no provider is available. + """ + providers = [provider] if provider else ["azure-ai-inference", "openai", "copilot-sdk"] + for p in providers: + client = _try_init_provider(p, endpoint, api_key, model) + if client is not None: + return client + return None + + +def _wrap_with_logging(raw_complete, provider_name, model_name): + import time + + log = [] + + def complete(system_prompt, user_prompt): + start = time.time() + response = raw_complete(system_prompt, user_prompt) + elapsed = time.time() - start + log.append( + { + "provider": provider_name, + "model": model_name, + "system_prompt": system_prompt, + "user_prompt": user_prompt, + "response": response, + "elapsed_seconds": round(elapsed, 2), + } + ) + return response + + complete.log = log + complete.provider_name = provider_name + complete.model_name = model_name + return complete + + +def _try_init_provider(name, endpoint, api_key, model): + if name == "azure-ai-inference": + return _init_azure_ai(endpoint, api_key, model) + elif name == "openai": + return _init_openai(endpoint, api_key, model) + elif name == "copilot-sdk": + return _init_copilot_sdk() + return None + + +def _init_azure_ai(endpoint, api_key, model): + try: + from azure.ai.inference import ChatCompletionsClient + from azure.ai.inference.models import SystemMessage, UserMessage + from azure.core.credentials import AzureKeyCredential + except ImportError: + return None + + if not endpoint or not api_key: + return None + + client = ChatCompletionsClient(endpoint=endpoint, credential=AzureKeyCredential(api_key)) + + def complete(system_prompt, user_prompt): + response = client.complete( + messages=[SystemMessage(content=system_prompt), UserMessage(content=user_prompt)], + max_tokens=150, + temperature=0.3, + ) + return response.choices[0].message.content.strip() + + print("[INFO] LLM provider: Azure AI Inference") + return _wrap_with_logging(complete, "Azure AI Inference", model) + + +def _init_openai(endpoint, api_key, model): + try: + import openai + except ImportError: + return None + + if not api_key: + return None + + if endpoint: + client = openai.AzureOpenAI(azure_endpoint=endpoint, api_key=api_key, api_version="2024-02-01") + else: + client = openai.OpenAI(api_key=api_key) + + def complete(system_prompt, user_prompt): + response = client.chat.completions.create( + model=model, + messages=[ + {"role": "system", "content": system_prompt}, + {"role": "user", "content": user_prompt}, + ], + max_tokens=150, + temperature=0.3, + ) + return response.choices[0].message.content.strip() + + provider_name = "Azure OpenAI" if endpoint else "OpenAI" + print(f"[INFO] LLM provider: {provider_name}") + return _wrap_with_logging(complete, provider_name, model) + + +def _init_copilot_sdk(): + # Uncomment and configure to use your Copilot subscription as the LLM provider. + # from copilot import CopilotClient + # ... + return None + + +# ================================================================ +# Configuration +# ================================================================ + +TABLE_ACCOUNTS = "account" +TABLE_CASES = "incident" +TABLE_OPPORTUNITIES = "opportunity" + +RISK_HIGH = 75 +RISK_MEDIUM = 40 + +_SCRIPT_DIR = Path(__file__).resolve().parent +OUTPUT_DIR = _SCRIPT_DIR / "risk_assessment_output" + + +async def main(): + """Entry point -- authenticate and run the async pipeline.""" + base_url = input("Enter Dataverse org URL (e.g. https://yourorg.crm.dynamics.com): ").strip() + if not base_url: + print("[ERR] No URL entered; exiting.") + sys.exit(1) + base_url = base_url.rstrip("/") + + print("[INFO] Authenticating via browser...") + credential = AsyncInteractiveBrowserCredential() + try: + async with AsyncDataverseClient(base_url, credential) as client: + await run_risk_pipeline(client) + finally: + await credential.close() + + +# ================================================================ +# Step 1: Extract -- Pull data concurrently with asyncio.gather +# ================================================================ + + +async def step1_extract(client): + """Extract accounts, cases, and opportunities concurrently.""" + print("\n" + "=" * 60) + print("STEP 1: Extract data from Dataverse (concurrently)") + print("=" * 60) + + # All three queries run in parallel -- significant speedup vs sequential. + accounts_result, cases_result, opps_result = await asyncio.gather( + client.query.builder(TABLE_ACCOUNTS) + .select("accountid", "name", "revenue", "numberofemployees", "industrycode") + .where(col("statecode") == 0) + .top(200) + .execute(), + client.query.builder(TABLE_CASES) + .select("incidentid", "_customerid_value", "title", "severitycode", "prioritycode", "createdon") + .where(raw("statecode eq 0")) + .top(1000) + .execute(), + client.query.builder(TABLE_OPPORTUNITIES) + .select( + "opportunityid", + "_parentaccountid_value", + "name", + "estimatedvalue", + "closeprobability", + "estimatedclosedate", + ) + .where(col("statecode") == 0) + .top(1000) + .execute(), + ) + + accounts = accounts_result.to_dataframe() + cases = cases_result.to_dataframe() + opportunities = opps_result.to_dataframe() + + print(f"[OK] Extracted {len(accounts)} active accounts") + print(f"[OK] Extracted {len(cases)} open cases") + print(f"[OK] Extracted {len(opportunities)} active opportunities") + + return accounts, cases, opportunities + + +# ================================================================ +# Step 2: Transform & Analyze -- Statistical risk scoring +# ================================================================ + + +def step2_analyze(accounts, cases, opportunities): + """Compute risk scores using pandas statistical operations (pure Python, unchanged).""" + print("\n" + "=" * 60) + print("STEP 2: Statistical analysis -- compute risk scores") + print("=" * 60) + + if not cases.empty and "_customerid_value" in cases.columns: + case_stats = ( + cases.groupby("_customerid_value") + .agg( + total_cases=("incidentid", "count"), + high_severity_cases=("severitycode", lambda x: (x == 1).sum()), + avg_priority=("prioritycode", "mean"), + ) + .reset_index() + .rename(columns={"_customerid_value": "accountid"}) + ) + else: + case_stats = pd.DataFrame(columns=["accountid", "total_cases", "high_severity_cases", "avg_priority"]) + + if not opportunities.empty and "_parentaccountid_value" in opportunities.columns: + opportunities = opportunities.copy() + opportunities["_weighted_value"] = ( + pd.to_numeric(opportunities["estimatedvalue"], errors="coerce").fillna(0) + * pd.to_numeric(opportunities["closeprobability"], errors="coerce").fillna(0) + / 100 + ) + opp_stats = ( + opportunities.groupby("_parentaccountid_value") + .agg( + total_opportunities=("opportunityid", "count"), + pipeline_value=("estimatedvalue", "sum"), + avg_close_probability=("closeprobability", "mean"), + weighted_pipeline=("_weighted_value", "sum"), + ) + .reset_index() + .rename(columns={"_parentaccountid_value": "accountid"}) + ) + else: + opp_stats = pd.DataFrame( + columns=[ + "accountid", + "total_opportunities", + "pipeline_value", + "avg_close_probability", + "weighted_pipeline", + ] + ) + + risk_df = accounts.merge(case_stats, on="accountid", how="left") + risk_df = risk_df.merge(opp_stats, on="accountid", how="left") + + for c in ["revenue", "numberofemployees"]: + if c in risk_df.columns: + risk_df[c] = pd.to_numeric(risk_df[c], errors="coerce").fillna(0) + + for c in ["total_cases", "high_severity_cases"]: + risk_df[c] = pd.to_numeric(risk_df[c], errors="coerce").fillna(0).astype(int) + for c in ["avg_priority", "pipeline_value", "avg_close_probability", "weighted_pipeline"]: + risk_df[c] = pd.to_numeric(risk_df[c], errors="coerce").fillna(0).astype(float) + risk_df["total_opportunities"] = ( + pd.to_numeric(risk_df["total_opportunities"], errors="coerce").fillna(0).astype(int) + ) + + risk_df["risk_score"] = compute_risk_score(risk_df) + risk_df["risk_tier"] = risk_df["risk_score"].apply(classify_risk) + + print(f"[OK] Computed risk scores for {len(risk_df)} accounts") + print(f" High risk: {(risk_df['risk_tier'] == 'High').sum()}") + print(f" Medium risk: {(risk_df['risk_tier'] == 'Medium').sum()}") + print(f" Low risk: {(risk_df['risk_tier'] == 'Low').sum()}") + + print("\n Risk score distribution:") + print(f" Mean: {risk_df['risk_score'].mean():.1f}") + print(f" Median: {risk_df['risk_score'].median():.1f}") + print(f" Std: {risk_df['risk_score'].std():.1f}") + print(f" Min: {risk_df['risk_score'].min():.1f}") + print(f" Max: {risk_df['risk_score'].max():.1f}") + + return risk_df + + +def compute_risk_score(df): + """Compute a 0-100 risk score from multiple factors.""" + scores = pd.Series(0.0, index=df.index) + + case_total = df["total_cases"].clip(lower=1) + severity_ratio = df["high_severity_cases"] / case_total + scores += severity_ratio * 35 + + if df["total_cases"].max() > 0: + case_pctile = df["total_cases"].rank(pct=True) + scores += case_pctile * 25 + else: + scores += 12.5 + + max_pipeline = df["weighted_pipeline"].max() + if max_pipeline > 0: + pipeline_strength = df["weighted_pipeline"] / max_pipeline + scores += (1 - pipeline_strength) * 20 + else: + scores += 10 + + close_risk = (100 - df["avg_close_probability"]) / 100 + scores += close_risk * 20 + + return scores.clip(0, 100).round(1) + + +def classify_risk(score): + if score >= RISK_HIGH: + return "High" + elif score >= RISK_MEDIUM: + return "Medium" + return "Low" + + +# ================================================================ +# Step 3: LLM Summarization +# ================================================================ + + +def step3_summarize(risk_df, llm_complete=None): + """Generate per-account risk summaries using LLM or template fallback.""" + print("\n" + "=" * 60) + print("STEP 3: Generate risk summaries") + print("=" * 60) + + flagged = risk_df[risk_df["risk_tier"].isin(["High", "Medium"])].copy() + print(f"[INFO] Generating summaries for {len(flagged)} flagged accounts") + + if llm_complete is not None: + summaries = _summarize_with_llm(flagged, llm_complete) + if hasattr(llm_complete, "log") and llm_complete.log: + _export_llm_log(llm_complete) + else: + print("[INFO] No LLM provider configured -- using template-based summarization") + summaries = _summarize_with_template(flagged) + + flagged["risk_summary"] = summaries + summary_map = dict(zip(flagged["accountid"], flagged["risk_summary"])) + risk_df["risk_summary"] = risk_df["accountid"].map(summary_map).fillna("Low risk -- no action needed.") + + print(f"[OK] Generated {len(summaries)} risk summaries") + + top_risk = risk_df.nlargest(3, "risk_score") + for _, row in top_risk.iterrows(): + print(f"\n Account: {row.get('name', 'Unknown')}") + print(f" Risk Score: {row['risk_score']} ({row['risk_tier']})") + print(f" Summary: {row['risk_summary'][:120]}...") + + return risk_df + + +def _summarize_with_llm(flagged_df, llm_complete): + system_prompt = ( + "You are a customer risk analyst at a financial services company. " + "Write exactly 2-3 sentences per account. " + "Sentence 1: State the risk level and primary driver. " + "Sentence 2: Quantify the key metric(s) behind the risk. " + "Sentence 3 (if needed): Recommend one specific action. " + "Use plain business language. Do not use bullet points or markdown." + ) + + summaries = [] + for _, row in flagged_df.iterrows(): + user_prompt = dedent(f"""\ + Summarize the risk for this account: + + Account Name: {row.get("name", "Unknown")} + Risk Score: {row["risk_score"]:.0f}/100 ({row["risk_tier"]} risk) + Open Support Cases: {row["total_cases"]} total, {row["high_severity_cases"]} high-severity + Revenue Pipeline: ${row["pipeline_value"]:,.0f} total, ${row["weighted_pipeline"]:,.0f} probability-weighted + Average Deal Close Probability: {row["avg_close_probability"]:.0f}% + """) + summaries.append(llm_complete(system_prompt, user_prompt)) + + return summaries + + +def _summarize_with_template(flagged_df): + summaries = [] + for _, row in flagged_df.iterrows(): + name = row.get("name", "Unknown") + parts = [] + + if row["high_severity_cases"] > 0: + parts.append(f"{row['high_severity_cases']} high-severity cases require immediate attention") + if row["total_cases"] > 5: + parts.append(f"elevated case volume ({row['total_cases']} open)") + if row["weighted_pipeline"] < 10000: + parts.append("weak revenue pipeline") + if row["avg_close_probability"] < 30: + parts.append(f"low close probability ({row['avg_close_probability']:.0f}%)") + if not parts: + parts.append("multiple moderate risk factors detected") + + summary = ( + f"{name} has a {row['risk_tier'].lower()} risk score of " + f"{row['risk_score']:.0f}/100. Key factors: {'; '.join(parts)}. " + f"Recommend proactive outreach and account review." + ) + summaries.append(summary) + + return summaries + + +def _export_llm_log(llm_complete, include_prompts=False): + log_path = OUTPUT_DIR / "llm_interactions.txt" + with open(log_path, "w", encoding="utf-8") as f: + f.write("LLM Interaction Log\n") + f.write("=" * 70 + "\n") + f.write(f"Provider: {llm_complete.provider_name}\n") + f.write(f"Model: {llm_complete.model_name}\n") + f.write(f"Total calls: {len(llm_complete.log)}\n") + total_time = sum(entry["elapsed_seconds"] for entry in llm_complete.log) + f.write(f"Total time: {total_time:.1f}s\n") + f.write("=" * 70 + "\n\n") + + for i, entry in enumerate(llm_complete.log, 1): + f.write(f"--- Call {i} ({entry['elapsed_seconds']:.2f}s) ---\n\n") + if include_prompts: + f.write(f"[System Prompt]\n{entry['system_prompt']}\n\n") + f.write(f"[User Prompt]\n{entry['user_prompt']}\n\n") + f.write(f"[Response]\n{entry['response']}\n\n") + else: + f.write(f"[Response length: {len(entry['response'])} chars]\n\n") + + print(f"[OK] LLM interaction log saved to {log_path}") + + +# ================================================================ +# Step 4: Write-back +# ================================================================ + + +async def step4_writeback(client, risk_df): + """Write risk scores and summaries back to Dataverse accounts.""" + print("\n" + "=" * 60) + print("STEP 4: Write risk assessments back to Dataverse") + print("=" * 60) + + update_df = risk_df[["accountid", "description"]].copy() + update_df["description"] = risk_df.apply( + lambda r: f"[Risk: {r['risk_tier']} ({r['risk_score']:.0f}/100)] {r['risk_summary']}", + axis=1, + ) + + await client.dataframe.update(TABLE_ACCOUNTS, update_df, id_column="accountid") + print(f"[OK] Updated {len(update_df)} account records with risk assessments") + + +# ================================================================ +# Step 5: Report +# ================================================================ + + +def step5_report(risk_df): + """Generate a summary report with optional visualization.""" + print("\n" + "=" * 60) + print("STEP 5: Risk assessment report") + print("=" * 60) + + tier_summary = ( + risk_df.groupby("risk_tier") + .agg( + count=("accountid", "count"), + avg_score=("risk_score", "mean"), + total_cases=("total_cases", "sum"), + total_pipeline=("pipeline_value", "sum"), + ) + .round(1) + ) + print("\nRisk Tier Summary:") + print(tier_summary.to_string()) + + top10 = risk_df.nlargest(10, "risk_score")[ + ["name", "risk_score", "risk_tier", "total_cases", "high_severity_cases", "pipeline_value"] + ] + print("\nTop 10 Highest Risk Accounts:") + print(top10.to_string(index=False)) + + if HAS_MATPLOTLIB: + _generate_charts(risk_df) + else: + print("\n[INFO] Install matplotlib for risk visualization charts") + + risk_df.to_csv(OUTPUT_DIR / "risk_scores.csv", index=False) + top10.to_csv(OUTPUT_DIR / "top10_risk.csv", index=False) + tier_summary.to_csv(OUTPUT_DIR / "tier_summary.csv") + print(f"\n[OK] Exported CSV reports to {OUTPUT_DIR}/") + + print("\n[OK] Risk assessment pipeline complete!") + + +def _generate_charts(risk_df): + fig, axes = plt.subplots(1, 3, figsize=(16, 5)) + fig.suptitle("Customer Account Risk Assessment", fontsize=14, fontweight="bold") + + axes[0].hist(risk_df["risk_score"], bins=20, color="#4472C4", edgecolor="white") + axes[0].axvline(RISK_HIGH, color="red", linestyle="--", label=f"High ({RISK_HIGH})") + axes[0].axvline(RISK_MEDIUM, color="orange", linestyle="--", label=f"Medium ({RISK_MEDIUM})") + axes[0].set_title("Risk Score Distribution") + axes[0].set_xlabel("Risk Score") + axes[0].set_ylabel("Number of Accounts") + axes[0].legend() + + tier_counts = risk_df["risk_tier"].value_counts() + colors = {"High": "#FF4444", "Medium": "#FFA500", "Low": "#44BB44"} + axes[1].pie( + tier_counts.values, + labels=tier_counts.index, + colors=[colors.get(t, "#888") for t in tier_counts.index], + autopct="%1.0f%%", + startangle=90, + ) + axes[1].set_title("Risk Tier Breakdown") + + axes[2].scatter( + risk_df["total_cases"], + risk_df["pipeline_value"], + c=risk_df["risk_score"], + cmap="RdYlGn_r", + alpha=0.7, + edgecolors="gray", + s=60, + ) + axes[2].set_title("Cases vs Pipeline (color = risk)") + axes[2].set_xlabel("Open Cases") + axes[2].set_ylabel("Pipeline Value ($)") + + plt.tight_layout() + chart_path = OUTPUT_DIR / "risk_assessment_report.png" + plt.savefig(chart_path, dpi=150, bbox_inches="tight") + print(f"[OK] Saved {chart_path}") + + +# ================================================================ +# Pipeline Orchestrator +# ================================================================ + + +async def run_risk_pipeline(client): + """Run the full async risk assessment pipeline.""" + OUTPUT_DIR.mkdir(exist_ok=True) + print(f"[INFO] Output folder: {OUTPUT_DIR.resolve()}") + + print("\n" + "#" * 60) + print(" ASYNC CUSTOMER RISK ASSESSMENT PIPELINE") + print(" Dataverse SDK (async) -> Pandas -> Analysis -> LLM -> Write-back") + print("#" * 60) + + # Step 1: Extract data concurrently + accounts, cases, opportunities = await step1_extract(client) + + if accounts.empty: + print("[WARN] No accounts found -- nothing to analyze.") + return + + # Step 2: Statistical analysis (pure Python -- synchronous) + risk_df = step2_analyze(accounts, cases, opportunities) + + # Step 3: LLM-powered risk summarization (synchronous LLM calls) + # Configure your LLM provider (uncomment one): + # Option A: Azure AI Inference + # llm = get_llm_client("azure-ai-inference", endpoint="https://...", api_key="...") + # Option B: OpenAI + # llm = get_llm_client("openai", api_key="sk-...") + # Option C: Azure OpenAI (via openai package) + # llm = get_llm_client("openai", endpoint="https://...", api_key="...") + llm = None # Set to get_llm_client(...) to enable LLM summarization + risk_df = step3_summarize(risk_df, llm_complete=llm) + + # Step 4: Write results back to Dataverse (async) + # Uncomment the next line to write back (requires custom columns on account table) + # await step4_writeback(client, risk_df) + print("\n[INFO] Step 4 (write-back) is commented out by default.") + print(" Uncomment step4_writeback() after adding custom columns to account table.") + + # Step 5: Generate summary report + charts (synchronous) + step5_report(risk_df) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/aio/advanced/fetchxml.py b/examples/aio/advanced/fetchxml.py new file mode 100644 index 00000000..95fd8811 --- /dev/null +++ b/examples/aio/advanced/fetchxml.py @@ -0,0 +1,574 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +""" +Async end-to-end FetchXML examples for Dataverse. + +Async equivalent of examples/advanced/fetchxml.py. + +Demonstrates ``await client.query.fetchxml().execute()`` and +``async for page in client.query.fetchxml().execute_pages()`` across +scenarios where FetchXML is required or preferred over OData/SQL: + +- Basic attribute queries +- operators (eq, like, in, null, not-null, between) +- (inner and outer joins) +- Ordering +- Page-size control with automatic paging-cookie propagation +- Aggregate queries (count, sum, avg, min, max, group-by) +- Built-in system tables (account → contact join) + +FetchXML is the right tool when: +- You need a JOIN type OData $expand cannot express (many-to-many, outer link) +- You need server-side aggregates (count, sum, avg) without GROUP BY SQL +- You need ```` operators unavailable in OData ($filter) + +Prerequisites: +- pip install PowerPlatform-Dataverse-Client azure-identity +""" + +import asyncio +import sys + +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parents[1])) +from _auth import AsyncInteractiveBrowserCredential +from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient +from PowerPlatform.Dataverse.core.errors import MetadataError + + +def log_call(description): + print(f"\n-> {description}") + + +def heading(section_num, title): + print(f"\n{'=' * 80}") + print(f"{section_num}. {title}") + print("=" * 80) + + +async def backoff(coro_fn, *, delays=(0, 2, 5, 10, 20, 20)): + """Retry a coroutine with exponential back-off.""" + last = None + total_delay = 0 + attempts = 0 + for d in delays: + if d: + await asyncio.sleep(d) + total_delay += d + attempts += 1 + try: + result = await coro_fn() + if attempts > 1: + print(f" [INFO] Backoff succeeded after {attempts - 1} retry(s); waited {total_delay}s total.") + return result + except Exception as ex: + last = ex + continue + if last: + if attempts: + print( + f" [WARN] Backoff exhausted after {max(attempts - 1, 0)} retry(s); waited {total_delay}s total." + f"\n [ERROR] {last}" + ) + raise last + + +async def main(): + print("=" * 80) + print("Dataverse SDK -- Async FetchXML End-to-End Examples") + print("=" * 80) + + heading(1, "Setup & Authentication") + base_url = input("Enter Dataverse org URL (e.g. https://yourorg.crm.dynamics.com): ").strip() + if not base_url: + print("No URL entered; exiting.") + sys.exit(1) + base_url = base_url.rstrip("/") + + log_call("AsyncInteractiveBrowserCredential()") + credential = AsyncInteractiveBrowserCredential() + + log_call(f"AsyncDataverseClient(base_url='{base_url}', credential=...)") + try: + async with AsyncDataverseClient(base_url=base_url, credential=credential) as client: + print(f"[OK] Connected to: {base_url}") + await _run_examples(client) + finally: + await credential.close() + + +async def _run_examples(client): + project_table = "new_FXDemoProject" + task_table = "new_FXDemoTask" + + # =================================================================== + # 2. Create tables and seed data + # =================================================================== + heading(2, "Create Tables & Seed Data") + + log_call(f"await client.tables.get('{project_table}')") + if await client.tables.get(project_table): + print(f"[OK] Table already exists: {project_table}") + else: + log_call(f"await client.tables.create('{project_table}', ...)") + try: + await backoff( + lambda: client.tables.create( + project_table, + { + "new_Code": "string", + "new_Budget": "decimal", + "new_Active": "bool", + "new_Region": "int", + }, + ) + ) + print(f"[OK] Created table: {project_table}") + except Exception as e: + if "already exists" in str(e).lower() or "not unique" in str(e).lower(): + print(f"[OK] Table already exists: {project_table} (skipped)") + else: + raise + + log_call(f"await client.tables.get('{task_table}')") + if await client.tables.get(task_table): + print(f"[OK] Table already exists: {task_table}") + else: + log_call(f"await client.tables.create('{task_table}', ...)") + try: + await backoff( + lambda: client.tables.create( + task_table, + { + "new_Title": "string", + "new_Hours": "int", + "new_Done": "bool", + "new_Priority": "int", + }, + ) + ) + print(f"[OK] Created table: {task_table}") + except Exception as e: + if "already exists" in str(e).lower() or "not unique" in str(e).lower(): + print(f"[OK] Table already exists: {task_table} (skipped)") + else: + raise + + print("\n[INFO] Creating lookup field: tasks → projects ...") + try: + await client.tables.create_lookup_field( + referencing_table=task_table, + lookup_field_name="new_ProjectId", + referenced_table=project_table, + display_name="Project", + ) + print("[OK] Created lookup: new_ProjectId on tasks → projects") + except Exception as e: + msg = str(e).lower() + if "already exists" in msg or "duplicate" in msg or "not unique" in msg: + print("[OK] Lookup already exists (skipped)") + else: + raise + + # Resolve entity set name for @odata.bind + project_set = f"{project_table.lower()}s" + try: + tinfo = await client.tables.get(project_table) + if tinfo: + project_set = tinfo.get("entity_set_name", project_set) + except Exception: + pass + + log_call(f"await client.records.create('{project_table}', [...])") + projects = [ + {"new_Code": "ALPHA", "new_Budget": 50000, "new_Active": True, "new_Region": 1}, + {"new_Code": "BRAVO", "new_Budget": 75000, "new_Active": True, "new_Region": 2}, + {"new_Code": "CHARLIE", "new_Budget": 30000, "new_Active": False, "new_Region": 3}, + {"new_Code": "DELTA", "new_Budget": 90000, "new_Active": True, "new_Region": 1}, + {"new_Code": "ECHO", "new_Budget": 42000, "new_Active": True, "new_Region": 2}, + ] + project_ids = await backoff(lambda: client.records.create(project_table, projects)) + print(f"[OK] Seeded {len(project_ids)} projects") + + log_call(f"await client.records.create('{task_table}', [...])") + tasks = [ + { + "new_Title": "Design mockups", + "new_Hours": 8, + "new_Done": True, + "new_Priority": 2, + "new_ProjectId@odata.bind": f"/{project_set}({project_ids[0]})", + }, + { + "new_Title": "Write unit tests", + "new_Hours": 12, + "new_Done": False, + "new_Priority": 3, + "new_ProjectId@odata.bind": f"/{project_set}({project_ids[0]})", + }, + { + "new_Title": "Code review", + "new_Hours": 3, + "new_Done": True, + "new_Priority": 1, + "new_ProjectId@odata.bind": f"/{project_set}({project_ids[1]})", + }, + { + "new_Title": "Deploy to staging", + "new_Hours": 5, + "new_Done": False, + "new_Priority": 3, + "new_ProjectId@odata.bind": f"/{project_set}({project_ids[1]})", + }, + { + "new_Title": "Update docs", + "new_Hours": 4, + "new_Done": True, + "new_Priority": 1, + "new_ProjectId@odata.bind": f"/{project_set}({project_ids[2]})", + }, + { + "new_Title": "Performance tuning", + "new_Hours": 10, + "new_Done": False, + "new_Priority": 2, + "new_ProjectId@odata.bind": f"/{project_set}({project_ids[3]})", + }, + { + "new_Title": "Security audit", + "new_Hours": 6, + "new_Done": False, + "new_Priority": 3, + "new_ProjectId@odata.bind": f"/{project_set}({project_ids[4]})", + }, + ] + task_ids = await backoff(lambda: client.records.create(task_table, tasks)) + print(f"[OK] Seeded {len(task_ids)} tasks") + + project_logical = project_table.lower() + task_logical = task_table.lower() + project_pk = f"{project_logical}id" + lookup_attr = "new_projectid" + + try: + # =================================================================== + # 3. Basic attribute query + # =================================================================== + heading(3, "Basic Attribute Query") + xml = f""" + + + + + + + + """ + log_call("await client.query.fetchxml(basic attribute query).execute()") + result = await backoff(lambda: client.query.fetchxml(xml).execute()) + print(f"[OK] {len(result)} projects:") + for r in result: + print(f" {r.get('new_code', ''):<10s} Budget={r.get('new_budget')} Active={r.get('new_active')}") + if result: + print(f" First by index: {result[0].get('new_code')}") + print(f" First by .first(): {result.first().get('new_code')}") + + # =================================================================== + # 4. operators + # =================================================================== + heading(4, " Operators") + + # eq + xml = f""" + + + + + + + """ + log_call('operator="eq" value="ALPHA"') + r = await backoff(lambda: client.query.fetchxml(xml).execute()) + print(f"[OK] eq: {[x.get('new_code') for x in r]}") + + # like + xml = f""" + + + + + + + """ + log_call('operator="like" value="%test%"') + r = await backoff(lambda: client.query.fetchxml(xml).execute()) + print(f"[OK] like: {len(r)} matches -> {[x.get('new_title') for x in r]}") + + # in + xml = f""" + + + + + + ALPHA + DELTA + + + + + """ + log_call('operator="in" values=[ALPHA, DELTA]') + r = await backoff(lambda: client.query.fetchxml(xml).execute()) + print(f"[OK] in: {[x.get('new_code') for x in r]}") + + # not-null + xml = f""" + + + + + + + """ + log_call('operator="not-null"') + r = await backoff(lambda: client.query.fetchxml(xml).execute()) + print(f"[OK] not-null: {len(r)} tasks have priority set") + + # between + xml = f""" + + + + + + + 40000 + 80000 + + + + + """ + log_call('operator="between" 40000 and 80000') + r = await backoff(lambda: client.query.fetchxml(xml).execute()) + print(f"[OK] between: {len(r)} projects -> {[(x.get('new_code'), x.get('new_budget')) for x in r]}") + + # =================================================================== + # 5. — inner join + # =================================================================== + heading(5, " Inner Join (Tasks → Projects)") + xml = f""" + + + + + + + + + + + """ + log_call("await client.query.fetchxml(link-entity inner join).execute()") + try: + result = await backoff(lambda: client.query.fetchxml(xml).execute()) + print(f"[OK] {len(result)} rows:") + for r in result: + print( + f" Task={r.get('new_title', ''):<25s} " + f"Hours={r.get('new_hours')} " + f"Project={r.get('p.new_code', '')} " + f"Budget={r.get('p.new_budget')}" + ) + except Exception as e: + print(f"[WARN] link-entity join failed: {e}") + + # =================================================================== + # 6. — outer join + # =================================================================== + heading(6, " Outer Join (Projects With or Without Tasks)") + xml = f""" + + + + + + + + + """ + log_call("await client.query.fetchxml(link-entity outer join).execute()") + try: + result = await backoff(lambda: client.query.fetchxml(xml).execute()) + print(f"[OK] {len(result)} rows (includes projects with no tasks):") + for r in result[:8]: + print(f" Project={r.get('new_code', ''):<10s} Task={r.get('t.new_title', '(none)')}") + except Exception as e: + print(f"[WARN] outer join failed: {e}") + + # =================================================================== + # 7. Ordering + # =================================================================== + heading(7, "Ordering ( element)") + xml = f""" + + + + + + + + """ + log_call("await client.query.fetchxml(order by hours DESC).execute()") + result = await backoff(lambda: client.query.fetchxml(xml).execute()) + print(f"[OK] Tasks by hours DESC:") + for r in result: + print(f" {r.get('new_title', ''):<25s} Hours={r.get('new_hours')}") + + # =================================================================== + # 8. Paging-cookie propagation + # =================================================================== + heading(8, "Paging-Cookie Propagation") + print( + "[INFO] 'count' sets the page size in FetchXML.\n" + "With count='2' and 7 seeded tasks the server returns pages of 2, 2, 2, 1.\n" + ".execute() collects all pages eagerly; .execute_pages() yields one QueryResult per HTTP page." + ) + xml_paged = f""" + + + + + + + + """ + log_call("await client.query.fetchxml(xml).execute() — eager, all pages collected") + result = await backoff(lambda: client.query.fetchxml(xml_paged).execute()) + print(f"[OK] execute(): {len(result)} total tasks (seeded {len(task_ids)}):") + for r in result: + print(f" {r.get('new_title', ''):<25s} Hours={r.get('new_hours')}") + + log_call("async for page in client.query.fetchxml(xml).execute_pages() — lazy, one QueryResult per page") + page_num = 0 + page_record_count = 0 + async for page in client.query.fetchxml(xml_paged).execute_pages(): + page_num += 1 + page_record_count += len(page) + print(f" Page {page_num}: {len(page)} record(s) — {[r.get('new_title') for r in page]}") + print(f"[OK] execute_pages(): {page_record_count} total tasks across {page_num} page(s)") + + # =================================================================== + # 9. Aggregates + # =================================================================== + heading(9, "Aggregate Queries ()") + + xml = f""" + + + + + + + + + + """ + log_call("await client.query.fetchxml(aggregate: count, sum, avg, min, max).execute()") + try: + result = await backoff(lambda: client.query.fetchxml(xml).execute()) + if result: + row = result.first() + print( + f"[OK] count={row.get('task_count')} sum={row.get('total_hours')} " + f"avg={row.get('avg_hours')} min={row.get('min_hours')} max={row.get('max_hours')}" + ) + except Exception as e: + print(f"[WARN] aggregate failed: {e}") + + xml = f""" + + + + + + + + + + """ + log_call("await client.query.fetchxml(aggregate group-by project).execute()") + try: + result = await backoff(lambda: client.query.fetchxml(xml).execute()) + print(f"[OK] Hours per project ({len(result)} groups):") + for r in result: + print( + f" {r.get('project_code', ''):<10s} " + f"Tasks={r.get('task_count')} " + f"Hours={r.get('total_hours')}" + ) + except Exception as e: + print(f"[WARN] group-by aggregate failed: {e}") + + # =================================================================== + # 10. Built-in system tables + # =================================================================== + heading(10, "Built-In System Tables (account → contact Join)") + xml = """ + + + + + + + + + """ + log_call("await client.query.fetchxml(account → contact inner join).execute()") + try: + result = await backoff(lambda: client.query.fetchxml(xml).execute()) + print(f"[OK] {len(result)} account-contact pairs:") + for r in result: + print(f" Account={r.get('name', ''):<25s} Contact={r.get('c.fullname', '')}") + except Exception as e: + print(f"[INFO] No account-contact data in this org: {e}") + + finally: + heading(11, "Cleanup") + for tbl in [task_table, project_table]: + log_call(f"await client.tables.delete('{tbl}')") + try: + await backoff(lambda tbl=tbl: client.tables.delete(tbl)) + print(f"[OK] Deleted table: {tbl}") + except Exception as ex: + if "404" in str(ex) or (isinstance(ex, MetadataError) and "not found" in str(ex).lower()): + print(f"[OK] Table already removed: {tbl}") + else: + print(f"[WARN] Could not delete {tbl}: {ex}") + + print("\n" + "=" * 80) + print("Async FetchXML Examples Complete!") + print("=" * 80) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/aio/advanced/file_upload.py b/examples/aio/advanced/file_upload.py new file mode 100644 index 00000000..e44f6a7c --- /dev/null +++ b/examples/aio/advanced/file_upload.py @@ -0,0 +1,361 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +""" +PowerPlatform Dataverse Client - Async File Upload Example + +Async equivalent of examples/advanced/file_upload.py. + +This example demonstrates file upload capabilities using the async +PowerPlatform-Dataverse-Client SDK with automatic chunking for large files. + +Prerequisites: + pip install PowerPlatform-Dataverse-Client + pip install azure-identity +""" + +import asyncio +import hashlib +import sys +import traceback +from pathlib import Path + +from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient + +sys.path.insert(0, str(Path(__file__).resolve().parents[1])) +from _auth import AsyncInteractiveBrowserCredential + +ATTRIBUTE_VISIBILITY_DELAYS = (0, 3, 10, 20, 35, 50, 70, 90, 120) + +# --- Helpers --- + +_FILE_HASH_CACHE: dict = {} + + +def file_sha256(path: Path): + """Return (hex_digest, size_bytes) for the file, with caching.""" + try: + cached = _FILE_HASH_CACHE.get(path) + if cached: + return cached + h = hashlib.sha256() + size = 0 + with path.open("rb") as f: + for chunk in iter(lambda: f.read(1024 * 1024), b""): + size += len(chunk) + h.update(chunk) + result = (h.hexdigest(), size) + _FILE_HASH_CACHE[path] = result + return result + except Exception: # noqa: BLE001 + return None, None + + +def generate_test_file(size_mb: int = 10) -> Path: + """Generate a dummy text file of the specified size for testing.""" + test_file = Path(__file__).resolve().parent / f"test_dummy_{size_mb}mb.txt" + target_size = size_mb * 1024 * 1024 + + line = b"The quick brown fox jumps over the lazy dog. " * 2 + b"\n" + with test_file.open("wb") as f: + written = 0 + while written < target_size: + chunk = line * min(1000, (target_size - written) // len(line) + 1) + chunk = chunk[: target_size - written] + f.write(chunk) + written += len(chunk) + + print({"test_file_generated": str(test_file), "size_mb": test_file.stat().st_size / (1024 * 1024)}) + return test_file + + +async def backoff(coro_fn, *, delays=(0, 2, 5, 10, 20, 20)): + """Retry an async operation with exponential back-off.""" + last = None + total_delay = 0 + attempts = 0 + for d in delays: + if d: + await asyncio.sleep(d) + total_delay += d + attempts += 1 + try: + result = await coro_fn() + if attempts > 1: + retry_count = attempts - 1 + print(f" [INFO] Backoff succeeded after {retry_count} retry(s); waited {total_delay}s total.") + return result + except Exception as ex: # noqa: BLE001 + last = ex + continue + if last: + if attempts: + retry_count = max(attempts - 1, 0) + print(f" [WARN] Backoff exhausted after {retry_count} retry(s); waited {total_delay}s total.") + raise last + + +# --- Table ensure --- +TABLE_SCHEMA_NAME = "new_FileSample" + + +async def ensure_table(client) -> dict: + """Get or create the demo table.""" + existing = await backoff(lambda: client.tables.get(TABLE_SCHEMA_NAME)) + if existing: + print({"table": TABLE_SCHEMA_NAME, "existed": True}) + return existing + info = await backoff(lambda: client.tables.create(TABLE_SCHEMA_NAME, {"new_Title": "string"})) + print({"table": TABLE_SCHEMA_NAME, "existed": False, "metadata_id": info.get("metadata_id")}) + return info + + +# --- Main --- + + +async def main(): + entered = input("Enter Dataverse org URL (e.g. https://yourorg.crm.dynamics.com): ").strip() + if not entered: + print("No URL entered; exiting.") + sys.exit(1) + base_url = entered.rstrip("/") + + # Mode selection (numeric): + # 1 = small (single PATCH <128MB) + # 2 = chunk (streaming for any size) + # 3 = all (small + chunk) + mode_raw = input("Choose mode: 1) small 2) chunk 3) all [default 3]: ").strip() + if not mode_raw: + mode_raw = "3" + if mode_raw not in {"1", "2", "3"}: + print({"invalid_mode": mode_raw, "fallback": 3}) + mode_raw = "3" + mode_int = int(mode_raw) + run_small = mode_int in (1, 3) + run_chunk = mode_int in (2, 3) + + delete_record_choice = input("Delete the created record at end? (Y/n): ").strip() or "y" + cleanup_record = delete_record_choice.lower() in ("y", "yes", "true", "1") + + delete_table_choice = input("Delete the table at end? (y/N): ").strip() or "n" + cleanup_table = delete_table_choice.lower() in ("y", "yes", "true", "1") + + credential = AsyncInteractiveBrowserCredential() + + # Generate test files before entering the async context + generated_10mb = generate_test_file(10) + generated_8mb = generate_test_file(8) + + try: + async with AsyncDataverseClient(base_url=base_url, credential=credential) as client: + + # --------------------------- Table ensure --------------------------- + try: + table_info = await ensure_table(client) + except Exception: # noqa: BLE001 + print("Table ensure failed:") + traceback.print_exc() + sys.exit(1) + + entity_set = table_info.get("entity_set_name") + table_schema_name = table_info.get("table_schema_name") + attr_prefix = table_schema_name.split("_", 1)[0] if "_" in table_schema_name else table_schema_name + name_attr = f"{attr_prefix}_name" + small_file_attr_schema = f"{attr_prefix}_SmallDocument" + chunk_file_attr_schema = f"{attr_prefix}_ChunkDocument" + + # --------------------------- Record create --------------------------- + record_id = None + try: + payload = {name_attr: "Async File Sample Record"} + print({"call": f"client.records.create('{table_schema_name}', payload)"}) + record_id = await backoff(lambda: client.records.create(table_schema_name, payload)) + print({"record_created": True, "id": record_id, "table schema name": table_schema_name}) + except Exception as e: # noqa: BLE001 + print({"record_created": False, "error": str(e)}) + sys.exit(1) + + if not record_id: + print("No record id; aborting upload.") + sys.exit(1) + + # --------------------------- Small single-request upload --------------------------- + if run_small: + print("Small single-request upload demo:") + try: + src_hash, small_file_size = file_sha256(generated_10mb) + + await backoff( + lambda: client.files.upload( + table=table_schema_name, + record_id=record_id, + file_column=small_file_attr_schema, + path=str(generated_10mb), + mode="small", + ) + ) + print({"small_upload_completed": True, "small_source_size": small_file_size}) + + # Download and verify via internal OData client + async with client._scoped_odata() as od: + dl_url = f"{od.api}/{entity_set}({record_id})/{small_file_attr_schema.lower()}/$value" + resp = await od._request("get", dl_url) + content = await resp.read() if hasattr(resp, "read") else (resp.content or b"") + + downloaded_hash = hashlib.sha256(content).hexdigest() if content else None + hash_match = (downloaded_hash == src_hash) if (downloaded_hash and src_hash) else None + print( + { + "small_file_source_size": small_file_size, + "small_file_download_size": len(content), + "small_file_size_match": len(content) == small_file_size, + "small_file_source_sha256_prefix": src_hash[:16] if src_hash else None, + "small_file_download_sha256_prefix": downloaded_hash[:16] if downloaded_hash else None, + "small_file_hash_match": hash_match, + } + ) + + # Replace with 8MB file + print("Small single-request upload demo - REPLACE with 8MB file:") + replace_hash, replace_size = file_sha256(generated_8mb) + await backoff( + lambda: client.files.upload( + table=table_schema_name, + record_id=record_id, + file_column=small_file_attr_schema, + path=str(generated_8mb), + mode="small", + if_none_match=False, + ) + ) + print({"small_replace_upload_completed": True, "small_replace_source_size": replace_size}) + + async with client._scoped_odata() as od: + dl_url = f"{od.api}/{entity_set}({record_id})/{small_file_attr_schema.lower()}/$value" + resp_r = await od._request("get", dl_url) + content_r = await resp_r.read() if hasattr(resp_r, "read") else (resp_r.content or b"") + + dl_hash_r = hashlib.sha256(content_r).hexdigest() if content_r else None + hash_match_r = (dl_hash_r == replace_hash) if (dl_hash_r and replace_hash) else None + print( + { + "small_replace_source_size": replace_size, + "small_replace_download_size": len(content_r), + "small_replace_size_match": len(content_r) == replace_size, + "small_replace_source_sha256_prefix": replace_hash[:16] if replace_hash else None, + "small_replace_download_sha256_prefix": dl_hash_r[:16] if dl_hash_r else None, + "small_replace_hash_match": hash_match_r, + } + ) + except Exception as ex: # noqa: BLE001 + print({"single_upload_failed": str(ex)}) + + # --------------------------- Chunk (streaming) upload --------------------------- + if run_chunk: + print("Streaming chunk upload demo (mode='chunk'):") + try: + src_hash_chunk, src_size_chunk = file_sha256(generated_10mb) + + await backoff( + lambda: client.files.upload( + table=table_schema_name, + record_id=record_id, + file_column=chunk_file_attr_schema, + path=str(generated_10mb), + mode="chunk", + ) + ) + print({"chunk_upload_completed": True}) + + async with client._scoped_odata() as od: + dl_url = f"{od.api}/{entity_set}({record_id})/{chunk_file_attr_schema.lower()}/$value" + resp = await od._request("get", dl_url) + content_chunk = await resp.read() if hasattr(resp, "read") else (resp.content or b"") + + dst_hash_chunk = hashlib.sha256(content_chunk).hexdigest() if content_chunk else None + hash_match_chunk = ( + (dst_hash_chunk == src_hash_chunk) if (dst_hash_chunk and src_hash_chunk) else None + ) + print( + { + "chunk_source_size": src_size_chunk, + "chunk_download_size": len(content_chunk), + "chunk_size_match": len(content_chunk) == src_size_chunk, + "chunk_source_sha256_prefix": src_hash_chunk[:16] if src_hash_chunk else None, + "chunk_download_sha256_prefix": dst_hash_chunk[:16] if dst_hash_chunk else None, + "chunk_hash_match": hash_match_chunk, + } + ) + + # Replace with 8MB file + print("Streaming chunk upload demo - REPLACE with 8MB file:") + replace_hash_c, replace_size_c = file_sha256(generated_8mb) + await backoff( + lambda: client.files.upload( + table=table_schema_name, + record_id=record_id, + file_column=chunk_file_attr_schema, + path=str(generated_8mb), + mode="chunk", + if_none_match=False, + ) + ) + print({"chunk_replace_upload_completed": True}) + + async with client._scoped_odata() as od: + dl_url = f"{od.api}/{entity_set}({record_id})/{chunk_file_attr_schema.lower()}/$value" + resp_rc = await od._request("get", dl_url) + content_rc = await resp_rc.read() if hasattr(resp_rc, "read") else (resp_rc.content or b"") + + dl_hash_rc = hashlib.sha256(content_rc).hexdigest() if content_rc else None + hash_match_rc = (dl_hash_rc == replace_hash_c) if (dl_hash_rc and replace_hash_c) else None + print( + { + "chunk_replace_source_size": replace_size_c, + "chunk_replace_download_size": len(content_rc), + "chunk_replace_size_match": len(content_rc) == replace_size_c, + "chunk_replace_source_sha256_prefix": replace_hash_c[:16] if replace_hash_c else None, + "chunk_replace_download_sha256_prefix": dl_hash_rc[:16] if dl_hash_rc else None, + "chunk_replace_hash_match": hash_match_rc, + } + ) + except Exception as ex: # noqa: BLE001 + print({"chunk_upload_failed": str(ex)}) + + # --------------------------- Cleanup --------------------------- + if cleanup_record and record_id: + try: + print({"call": f"client.records.delete('{table_schema_name}', '{record_id}')"}) + await backoff(lambda: client.records.delete(table_schema_name, record_id)) + print({"record_deleted": True}) + except Exception as e: # noqa: BLE001 + print({"record_deleted": False, "error": str(e)}) + else: + print({"record_deleted": False, "reason": "user opted to keep"}) + + if cleanup_table: + try: + print({"call": f"client.tables.delete('{TABLE_SCHEMA_NAME}')"}) + await backoff(lambda: client.tables.delete(TABLE_SCHEMA_NAME)) + print({"table_deleted": True}) + except Exception as e: # noqa: BLE001 + print({"table_deleted": False, "error": str(e)}) + else: + print({"table_deleted": False, "reason": "user opted to keep"}) + finally: + await credential.close() + + # Clean up generated test files + for f in [generated_10mb, generated_8mb]: + if f and f.exists(): + try: + f.unlink() + print({"test_file_deleted": True, "path": str(f)}) + except Exception as e: # noqa: BLE001 + print({"test_file_deleted": False, "error": str(e)}) + + print("Done.") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/aio/advanced/prodev_quick_start.py b/examples/aio/advanced/prodev_quick_start.py new file mode 100644 index 00000000..4cc953a8 --- /dev/null +++ b/examples/aio/advanced/prodev_quick_start.py @@ -0,0 +1,475 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +""" +PowerPlatform Dataverse Client - Async Pro-Dev Quick Start + +Async equivalent of examples/advanced/prodev_quick_start.py. + +A developer-focused example that demonstrates the full async SDK lifecycle: +install, authenticate, create a system with 4 related tables, populate +data, query it, and clean up -- all in a single script. + +What this example covers: + 1) SDK installation and authentication + 2) Create 4 custom tables concurrently with asyncio.gather() + 3) Create columns and relationships between tables + 4) Populate with sample data using async DataFrame CRUD + 5) Query and join data across tables + 6) Clean up (delete tables) + +Prerequisites: + pip install PowerPlatform-Dataverse-Client + pip install azure-identity +""" + +import asyncio +import sys +import uuid +import warnings +from pathlib import Path + +# Suppress MSAL advisory about response_mode (third-party library, not actionable here) +warnings.filterwarnings("ignore", message="response_mode=.*form_post", category=UserWarning) + +import pandas as pd + +sys.path.insert(0, str(Path(__file__).resolve().parents[1])) +from _auth import AsyncInteractiveBrowserCredential + +from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient +from PowerPlatform.Dataverse.models.filters import col + +# -- Table schema names -- +SUFFIX = uuid.uuid4().hex[:6] +TABLE_CUSTOMER = f"new_DemoCustomer{SUFFIX}" +TABLE_PROJECT = f"new_DemoProject{SUFFIX}" +TABLE_TASK = f"new_DemoTask{SUFFIX}" +TABLE_TIMEENTRY = f"new_DemoTimeEntry{SUFFIX}" + +# -- Output folder for exported data -- +_SCRIPT_DIR = Path(__file__).resolve().parent +OUTPUT_DIR = _SCRIPT_DIR / "prodev_output" + + +async def main(): + """Entry point.""" + print("=" * 60) + print(" DATAVERSE PYTHON SDK -- ASYNC PRO-DEV QUICK START") + print("=" * 60) + print() + + base_url = input("Enter Dataverse org URL (e.g. https://yourorg.crm.dynamics.com): ").strip() + if not base_url: + print("[ERR] No URL entered; exiting.") + sys.exit(1) + base_url = base_url.rstrip("/") + + print("[INFO] Authenticating via browser (Azure Identity)...") + credential = AsyncInteractiveBrowserCredential() + try: + async with AsyncDataverseClient(base_url, credential) as client: + try: + await run_demo(client) + except Exception as e: + print(f"\n[ERR] {e}") + print("[INFO] Attempting cleanup...") + await cleanup(client) + raise + finally: + await credential.close() + + +async def run_demo(client): + """Run the full async pro-dev demo pipeline.""" + OUTPUT_DIR.mkdir(exist_ok=True) + print(f"[INFO] Output folder: {OUTPUT_DIR.resolve()}") + + # -- Step 1: Create 4 tables concurrently -- + primary_name_col, primary_id_col = await step1_create_tables(client) + + # -- Step 2: Create relationships -- + await step2_create_relationships(client) + + # -- Step 3: Populate with sample data -- + customer_ids, project_ids, task_ids = await step3_populate_data(client, primary_name_col) + + # -- Step 4: Query and analyze -- + await step4_query_and_analyze(client, customer_ids, primary_name_col, primary_id_col) + + # -- Step 5: Update and delete -- + await step5_update_and_delete(client, task_ids, primary_name_col, primary_id_col) + + # -- Step 6: Cleanup -- + do_cleanup = input("\n6. Delete demo tables and cleanup? (Y/n): ").strip() or "y" + if do_cleanup.lower() in ("y", "yes"): + await cleanup(client) + else: + print("[INFO] Tables kept for inspection.") + + print("\n" + "=" * 60) + print("[OK] Async pro-dev quick start demo complete!") + print("=" * 60) + + +# ================================================================ +# Step 1: Create tables (concurrently with asyncio.gather) +# ================================================================ + + +async def step1_create_tables(client): + """Create 4 custom tables sequentially. + + Note: Dataverse holds a metadata customization lock for the duration of + each table-creation request. Concurrent creates (asyncio.gather) trigger + a CustomizationLockException on the server, so tables must be created one + at a time. + """ + print("\n" + "-" * 60) + print("STEP 1: Create 4 custom tables (sequentially)") + print("-" * 60) + + customer_result = await client.tables.create( + TABLE_CUSTOMER, + { + f"{TABLE_CUSTOMER}_Email": "string", + f"{TABLE_CUSTOMER}_Industry": "string", + f"{TABLE_CUSTOMER}_Revenue": "money", + }, + ) + await client.tables.create( + TABLE_PROJECT, + { + f"{TABLE_PROJECT}_Budget": "money", + f"{TABLE_PROJECT}_Status": "string", + f"{TABLE_PROJECT}_StartDate": "datetime", + }, + ) + await client.tables.create( + TABLE_TASK, + { + f"{TABLE_TASK}_Priority": "integer", + f"{TABLE_TASK}_Status": "string", + f"{TABLE_TASK}_EstimatedHours": "decimal", + }, + ) + await client.tables.create( + TABLE_TIMEENTRY, + { + f"{TABLE_TIMEENTRY}_Hours": "decimal", + f"{TABLE_TIMEENTRY}_Date": "datetime", + f"{TABLE_TIMEENTRY}_Description": "string", + }, + ) + + primary_name_col = customer_result.primary_name_attribute + primary_id_col = customer_result.primary_id_attribute + print(f"[OK] Created table: {TABLE_CUSTOMER} (name: {primary_name_col}, id: {primary_id_col})") + print(f"[OK] Created table: {TABLE_PROJECT}") + print(f"[OK] Created table: {TABLE_TASK}") + print(f"[OK] Created table: {TABLE_TIMEENTRY}") + print(f"[OK] All 4 tables created (suffix: {SUFFIX})") + + return primary_name_col, primary_id_col + + +# ================================================================ +# Step 2: Create relationships +# ================================================================ + + +async def step2_create_relationships(client): + """Create relationships between the 4 tables using lookup fields.""" + print("\n" + "-" * 60) + print("STEP 2: Create relationships (lookup fields)") + print("-" * 60) + + # Relationships must be created sequentially -- Dataverse rejects + # concurrent metadata writes to related tables. + await client.tables.create_lookup_field( + referencing_table=TABLE_PROJECT.lower(), + lookup_field_name=f"{TABLE_PROJECT}_CustomerId", + referenced_table=TABLE_CUSTOMER.lower(), + display_name="Customer", + ) + print(f"[OK] {TABLE_CUSTOMER} 1:N {TABLE_PROJECT}") + + await client.tables.create_lookup_field( + referencing_table=TABLE_TASK.lower(), + lookup_field_name=f"{TABLE_TASK}_ProjectId", + referenced_table=TABLE_PROJECT.lower(), + display_name="Project", + ) + print(f"[OK] {TABLE_PROJECT} 1:N {TABLE_TASK}") + + await client.tables.create_lookup_field( + referencing_table=TABLE_TIMEENTRY.lower(), + lookup_field_name=f"{TABLE_TIMEENTRY}_TaskId", + referenced_table=TABLE_TASK.lower(), + display_name="Task", + ) + print(f"[OK] {TABLE_TASK} 1:N {TABLE_TIMEENTRY}") + print("[OK] 3 lookup relationships created (Customer -> Project -> Task -> TimeEntry)") + + +# ================================================================ +# Step 3: Populate with sample data +# ================================================================ + + +async def step3_populate_data(client, primary_name_col): + """Create sample records using client.dataframe.create().""" + print("\n" + "-" * 60) + print("STEP 3: Populate with sample data (async DataFrame CRUD)") + print("-" * 60) + + # -- Customers -- + name_col = primary_name_col + customers_df = pd.DataFrame( + [ + { + name_col: "Contoso Ltd", + f"{TABLE_CUSTOMER}_Email": "info@contoso.com", + f"{TABLE_CUSTOMER}_Industry": "Technology", + f"{TABLE_CUSTOMER}_Revenue": 5000000, + }, + { + name_col: "Fabrikam Inc", + f"{TABLE_CUSTOMER}_Email": "contact@fabrikam.com", + f"{TABLE_CUSTOMER}_Industry": "Manufacturing", + f"{TABLE_CUSTOMER}_Revenue": 12000000, + }, + { + name_col: "Northwind Traders", + f"{TABLE_CUSTOMER}_Email": "sales@northwind.com", + f"{TABLE_CUSTOMER}_Industry": "Retail", + f"{TABLE_CUSTOMER}_Revenue": 3000000, + }, + ] + ) + customer_ids = await client.dataframe.create(TABLE_CUSTOMER, customers_df) + customers_df["id"] = customer_ids + print(f"[OK] Created {len(customers_df)} customers") + + # -- Projects (linked to customers via lookup) -- + customer_lookup = f"{TABLE_PROJECT}_CustomerId@odata.bind" + customer_info = await client.tables.get(TABLE_CUSTOMER) + customer_set = customer_info.get("entity_set_name") if customer_info else TABLE_CUSTOMER.lower() + "s" + projects_df = pd.DataFrame( + [ + { + name_col: "Cloud Migration", + f"{TABLE_PROJECT}_Budget": 250000, + f"{TABLE_PROJECT}_Status": "Active", + f"{TABLE_PROJECT}_StartDate": pd.Timestamp("2026-01-15"), + customer_lookup: f"/{customer_set}({customer_ids.iloc[0]})", + }, + { + name_col: "ERP Upgrade", + f"{TABLE_PROJECT}_Budget": 500000, + f"{TABLE_PROJECT}_Status": "Active", + f"{TABLE_PROJECT}_StartDate": pd.Timestamp("2026-02-01"), + customer_lookup: f"/{customer_set}({customer_ids.iloc[1]})", + }, + { + name_col: "POS Modernization", + f"{TABLE_PROJECT}_Budget": 150000, + f"{TABLE_PROJECT}_Status": "Planning", + f"{TABLE_PROJECT}_StartDate": pd.Timestamp("2026-03-01"), + customer_lookup: f"/{customer_set}({customer_ids.iloc[2]})", + }, + { + name_col: "Data Analytics Platform", + f"{TABLE_PROJECT}_Budget": 180000, + f"{TABLE_PROJECT}_Status": "Active", + f"{TABLE_PROJECT}_StartDate": pd.Timestamp("2026-01-20"), + customer_lookup: f"/{customer_set}({customer_ids.iloc[0]})", + }, + ] + ) + project_ids = await client.dataframe.create(TABLE_PROJECT, projects_df) + projects_df["id"] = project_ids + print(f"[OK] Created {len(projects_df)} projects across 3 customers") + + # -- Tasks (linked to projects) -- + task_names = [ + ("Infrastructure Setup", 1, "In Progress", 40), + ("Data Assessment", 2, "Not Started", 20), + ("Testing & QA", 1, "Not Started", 60), + ("Requirements Gathering", 1, "Complete", 30), + ("Development Sprint 1", 1, "In Progress", 80), + ("User Training", 3, "Not Started", 16), + ] + project_assignment = [0, 0, 0, 1, 1, 2] + + project_info = await client.tables.get(TABLE_PROJECT) + project_set = project_info.get("entity_set_name") if project_info else TABLE_PROJECT.lower() + "s" + project_lookup = f"{TABLE_TASK}_ProjectId@odata.bind" + + tasks_data = [ + { + name_col: task_name, + f"{TABLE_TASK}_Priority": priority, + f"{TABLE_TASK}_Status": status, + f"{TABLE_TASK}_EstimatedHours": hours, + project_lookup: f"/{project_set}({project_ids.iloc[project_assignment[i]]})", + } + for i, (task_name, priority, status, hours) in enumerate(task_names) + ] + + tasks_df = pd.DataFrame(tasks_data) + task_ids = await client.dataframe.create(TABLE_TASK, tasks_df) + tasks_df["id"] = task_ids + print(f"[OK] Created {len(tasks_df)} tasks across 4 projects") + + print( + f"\n Total records: {len(customers_df) + len(projects_df) + len(tasks_df)} " + f"({len(customers_df)} customers, {len(projects_df)} projects, {len(tasks_df)} tasks)" + ) + + return customer_ids, project_ids, task_ids + + +# ================================================================ +# Step 4: Query and analyze data +# ================================================================ + + +async def step4_query_and_analyze(client, customer_ids, primary_name_col, primary_id_col): + """Query data and demonstrate DataFrame analysis.""" + print("\n" + "-" * 60) + print("STEP 4: Query and analyze data") + print("-" * 60) + + name_attr = primary_name_col + + # Query projects and tasks concurrently + project_result, task_result = await asyncio.gather( + client.query.builder(TABLE_PROJECT) + .select(name_attr, f"{TABLE_PROJECT}_Budget", f"{TABLE_PROJECT}_Status") + .execute(), + client.query.builder(TABLE_TASK) + .select(name_attr, f"{TABLE_TASK}_Priority", f"{TABLE_TASK}_Status", f"{TABLE_TASK}_EstimatedHours") + .execute(), + ) + + projects = project_result.to_dataframe() + tasks = task_result.to_dataframe() + + print(f"\n All projects ({len(projects)} rows):") + print(f"{projects.to_string(index=False)}") + + print(f"\n All tasks ({len(tasks)} rows):") + print(f"{tasks.to_string(index=False)}") + + # -- DataFrame analysis -- + hours_col = f"{TABLE_TASK}_EstimatedHours" + status_col = f"{TABLE_TASK}_Status" + budget_col = f"{TABLE_PROJECT}_Budget" + + if hours_col in tasks.columns: + print(f"\n Task hours summary:") + print(f" Total estimated hours: {tasks[hours_col].sum():.0f}") + print(f" Average per task: {tasks[hours_col].mean():.1f}") + print(f" Max single task: {tasks[hours_col].max():.0f}") + + if status_col in tasks.columns: + print(f"\n Tasks by status:") + for status, count in tasks[status_col].value_counts().items(): + print(f" {status}: {count}") + + if budget_col in projects.columns: + print(f"\n Project budget summary:") + print(f" Total budget: ${projects[budget_col].sum():,.0f}") + print(f" Average budget: ${projects[budget_col].mean():,.0f}") + + # Fetch single customer record by ID + first_id = customer_ids.iloc[0] + single_result = await client.query.builder(TABLE_CUSTOMER).where(col(primary_id_col) == first_id).execute() + single = single_result.to_dataframe() + print(f"\n Single customer record (by ID):") + print(f"{single.to_string(index=False)}") + + # -- Export query results to CSV -- + projects.to_csv(OUTPUT_DIR / "projects.csv", index=False) + tasks.to_csv(OUTPUT_DIR / "tasks.csv", index=False) + single.to_csv(OUTPUT_DIR / "single_customer.csv", index=False) + print(f"\n[OK] Exported query results to {OUTPUT_DIR}/") + + +# ================================================================ +# Step 5: Update and delete records +# ================================================================ + + +async def step5_update_and_delete(client, task_ids, primary_name_col, primary_id_col): + """Demonstrate update and delete with DataFrames.""" + print("\n" + "-" * 60) + print("STEP 5: Update and delete records") + print("-" * 60) + + status_col = f"{TABLE_TASK}_Status" + + # Update: mark first two tasks as "Complete" + update_df = pd.DataFrame( + { + primary_id_col: [task_ids.iloc[0], task_ids.iloc[1]], + status_col: ["Complete", "Complete"], + } + ) + await client.dataframe.update(TABLE_TASK, update_df, id_column=primary_id_col) + print(f"[OK] Updated 2 tasks to 'Complete'") + + # Delete: remove the last task + delete_ids = pd.Series([task_ids.iloc[-1]]) + await client.dataframe.delete(TABLE_TASK, delete_ids) + print(f"[OK] Deleted 1 task") + + # Verify + result = await client.query.builder(TABLE_TASK).select(primary_name_col, status_col).execute() + remaining = result.to_dataframe() + print(f"\n Remaining tasks ({len(remaining)}):") + print(f"{remaining.to_string(index=False)}") + + +# ================================================================ +# Cleanup +# ================================================================ + + +async def cleanup(client): + """Delete all demo tables. + + Tables must be deleted leaf-to-root (TimeEntry → Task → Project → Customer) + because each table holds a lookup field referencing the next. Dataverse may + also return transient SQL-deadlock errors during metadata operations, so we + retry failed deletions until all tables are gone or no further progress is + made. + """ + print("\n" + "-" * 60) + print("CLEANUP: Removing demo tables") + print("-" * 60) + + remaining = [TABLE_TIMEENTRY, TABLE_TASK, TABLE_PROJECT, TABLE_CUSTOMER] + while remaining: + failed = [] + for table in remaining: + try: + await client.tables.delete(table) + print(f"[OK] Deleted table: {table}") + except Exception as e: + failed.append((table, e)) + + if len(failed) == len(remaining): + # No progress — report and stop to avoid an infinite loop. + for table, e in failed: + print(f"[WARN] Could not delete {table}: {e}") + break + + remaining = [t for t, _ in failed] + + print("[OK] Cleanup complete") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/aio/advanced/relationships.py b/examples/aio/advanced/relationships.py new file mode 100644 index 00000000..a4f9ecff --- /dev/null +++ b/examples/aio/advanced/relationships.py @@ -0,0 +1,401 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +""" +Async Relationship Management Example for Dataverse SDK. + +Async equivalent of examples/advanced/relationships.py. + +This example demonstrates: +- Creating one-to-many relationships using the core SDK API +- Creating lookup fields using the convenience method +- Creating many-to-many relationships +- Querying and deleting relationships +- Working with relationship metadata types + +Prerequisites: +- pip install PowerPlatform-Dataverse-Client +- pip install azure-identity +""" + +import asyncio +import sys +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parents[1])) +from _auth import AsyncInteractiveBrowserCredential +from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient +from PowerPlatform.Dataverse.models.relationship import ( + LookupAttributeMetadata, + OneToManyRelationshipMetadata, + ManyToManyRelationshipMetadata, + CascadeConfiguration, +) +from PowerPlatform.Dataverse.models.labels import Label, LocalizedLabel +from PowerPlatform.Dataverse.common.constants import ( + CASCADE_BEHAVIOR_NO_CASCADE, + CASCADE_BEHAVIOR_REMOVE_LINK, +) + + +# Simple logging helper +def log_call(description): + print(f"\n-> {description}") + + +async def delete_relationship_if_exists(client, schema_name): + """Delete a relationship by schema name if it exists.""" + rel = await client.tables.get_relationship(schema_name) + if rel: + rel_id = rel.relationship_id + if rel_id: + await client.tables.delete_relationship(rel_id) + print(f" (Cleaned up existing relationship: {schema_name})") + return True + return False + + +async def cleanup_previous_run(client): + """Clean up any resources from a previous run to make the example idempotent.""" + print("\n-> Checking for resources from previous runs...") + + # Known relationship names created by this example + relationships = [ + "new_Department_Employee", + "contact_new_employee_new_ManagerId", + "new_employee_project", + ] + + # Known table names created by this example + tables = ["new_Employee", "new_Department", "new_Project"] + + # Delete relationships first (required before tables can be deleted) + for rel_name in relationships: + try: + await delete_relationship_if_exists(client, rel_name) + except Exception as e: + print(f" [WARN] Could not delete relationship {rel_name}: {e}") + + # Delete tables + for table_name in tables: + try: + if await client.tables.get(table_name): + await client.tables.delete(table_name) + print(f" (Cleaned up existing table: {table_name})") + except Exception as e: + print(f" [WARN] Could not delete table {table_name}: {e}") + + +async def backoff(coro_fn, *, delays=(0, 2, 5, 10, 20, 20)): + """Retry helper with exponential backoff.""" + last = None + total_delay = 0 + attempts = 0 + for d in delays: + if d: + await asyncio.sleep(d) + total_delay += d + attempts += 1 + try: + result = await coro_fn() + if attempts > 1: + retry_count = attempts - 1 + print(f" * Backoff succeeded after {retry_count} retry(s); waited {total_delay}s total.") + return result + except Exception as ex: # noqa: BLE001 + last = ex + continue + if last: + if attempts: + retry_count = max(attempts - 1, 0) + print(f" [WARN] Backoff exhausted after {retry_count} retry(s); waited {total_delay}s total.") + raise last + + +async def main(): + print("=" * 80) + print("Dataverse SDK - Async Relationship Management Example") + print("=" * 80) + + # ============================================================================ + # 1. SETUP & AUTHENTICATION + # ============================================================================ + print("\n" + "=" * 80) + print("1. Setup & Authentication") + print("=" * 80) + + base_url = input("Enter Dataverse org URL (e.g. https://yourorg.crm.dynamics.com): ").strip() + if not base_url: + print("No URL entered; exiting.") + sys.exit(1) + + base_url = base_url.rstrip("/") + + log_call("AsyncInteractiveBrowserCredential()") + credential = AsyncInteractiveBrowserCredential() + + log_call(f"AsyncDataverseClient(base_url='{base_url}', credential=...)") + try: + async with AsyncDataverseClient(base_url=base_url, credential=credential) as client: + print(f"[OK] Connected to: {base_url}") + await _run_example(client) + finally: + await credential.close() + + +async def _run_example(client): + # Initialize relationship IDs to None for cleanup safety + rel_id_1 = None + rel_id_2 = None + rel_id_3 = None + + # ============================================================================ + # 2. CLEANUP PREVIOUS RUN (Idempotency) + # ============================================================================ + print("\n" + "=" * 80) + print("2. Cleanup Previous Run (Idempotency)") + print("=" * 80) + + await cleanup_previous_run(client) + + # ============================================================================ + # 3. CREATE SAMPLE TABLES + # ============================================================================ + print("\n" + "=" * 80) + print("3. Create Sample Tables") + print("=" * 80) + + # Create a parent table (Department) + log_call("Creating 'new_Department' table") + + dept_table = await backoff( + lambda: client.tables.create( + "new_Department", + { + "new_DepartmentCode": "string", + "new_Budget": "decimal", + }, + ) + ) + print(f"[OK] Created table: {dept_table['table_schema_name']}") + + # Create a child table (Employee) + log_call("Creating 'new_Employee' table") + + emp_table = await backoff( + lambda: client.tables.create( + "new_Employee", + { + "new_EmployeeNumber": "string", + "new_Salary": "decimal", + }, + ) + ) + print(f"[OK] Created table: {emp_table['table_schema_name']}") + + # Create a project table for many-to-many example + log_call("Creating 'new_Project' table") + + proj_table = await backoff( + lambda: client.tables.create( + "new_Project", + { + "new_ProjectCode": "string", + "new_StartDate": "datetime", + }, + ) + ) + print(f"[OK] Created table: {proj_table['table_schema_name']}") + + # ============================================================================ + # 4. CREATE ONE-TO-MANY RELATIONSHIP (Core SDK API) + # ============================================================================ + print("\n" + "=" * 80) + print("4. Create One-to-Many Relationship (Core API)") + print("=" * 80) + + log_call("Creating lookup field on Employee referencing Department") + + # Define the lookup attribute metadata + lookup = LookupAttributeMetadata( + schema_name="new_DepartmentId", + display_name=Label(localized_labels=[LocalizedLabel(label="Department", language_code=1033)]), + required_level="None", + ) + + # Define the relationship metadata + relationship = OneToManyRelationshipMetadata( + schema_name="new_Department_Employee", + referenced_entity=dept_table["table_logical_name"], + referencing_entity=emp_table["table_logical_name"], + referenced_attribute=f"{dept_table['table_logical_name']}id", + cascade_configuration=CascadeConfiguration( + delete=CASCADE_BEHAVIOR_REMOVE_LINK, + assign=CASCADE_BEHAVIOR_NO_CASCADE, + merge=CASCADE_BEHAVIOR_NO_CASCADE, + ), + ) + + # Create the relationship + result = await backoff( + lambda: client.tables.create_one_to_many_relationship( + lookup=lookup, + relationship=relationship, + ) + ) + + print(f"[OK] Created relationship: {result.relationship_schema_name}") + print(f" Lookup field: {result.lookup_schema_name}") + print(f" Relationship ID: {result.relationship_id}") + + rel_id_1 = result.relationship_id + + # ============================================================================ + # 5. CREATE LOOKUP FIELD (Convenience Method) + # ============================================================================ + print("\n" + "=" * 80) + print("5. Create Lookup Field (Convenience Method)") + print("=" * 80) + + log_call("Creating lookup field on Employee referencing Contact as Manager") + + # Use the convenience method for simpler scenarios + # An Employee has a Manager (who is a Contact in the system) + result2 = await backoff( + lambda: client.tables.create_lookup_field( + referencing_table=emp_table["table_logical_name"], + lookup_field_name="new_ManagerId", + referenced_table="contact", + display_name="Manager", + description="The employee's direct manager", + required=False, + cascade_delete=CASCADE_BEHAVIOR_REMOVE_LINK, + ) + ) + + print(f"[OK] Created lookup using convenience method: {result2.lookup_schema_name}") + print(f" Relationship: {result2.relationship_schema_name}") + + rel_id_2 = result2.relationship_id + + # ============================================================================ + # 6. CREATE MANY-TO-MANY RELATIONSHIP + # ============================================================================ + print("\n" + "=" * 80) + print("6. Create Many-to-Many Relationship") + print("=" * 80) + + log_call("Creating M:N relationship between Employee and Project") + + # Define many-to-many relationship + m2m_relationship = ManyToManyRelationshipMetadata( + schema_name="new_employee_project", + entity1_logical_name=emp_table["table_logical_name"], + entity2_logical_name=proj_table["table_logical_name"], + ) + + result3 = await backoff( + lambda: client.tables.create_many_to_many_relationship( + relationship=m2m_relationship, + ) + ) + + print(f"[OK] Created M:N relationship: {result3.relationship_schema_name}") + print(f" Relationship ID: {result3.relationship_id}") + + rel_id_3 = result3.relationship_id + + # ============================================================================ + # 7. QUERY RELATIONSHIP METADATA + # ============================================================================ + print("\n" + "=" * 80) + print("7. Query Relationship Metadata") + print("=" * 80) + + log_call("Retrieving 1:N relationship by schema name") + + rel_metadata = await client.tables.get_relationship("new_Department_Employee") + if rel_metadata: + print(f"[OK] Found relationship: {rel_metadata.relationship_schema_name}") + print(f" Type: {rel_metadata.relationship_type}") + print(f" Referenced Entity: {rel_metadata.referenced_entity}") + print(f" Referencing Entity: {rel_metadata.referencing_entity}") + else: + print(" Relationship not found") + + log_call("Retrieving M:N relationship by schema name") + + m2m_metadata = await client.tables.get_relationship("new_employee_project") + if m2m_metadata: + print(f"[OK] Found relationship: {m2m_metadata.relationship_schema_name}") + print(f" Type: {m2m_metadata.relationship_type}") + print(f" Entity 1: {m2m_metadata.entity1_logical_name}") + print(f" Entity 2: {m2m_metadata.entity2_logical_name}") + else: + print(" Relationship not found") + + # ============================================================================ + # 8. CLEANUP + # ============================================================================ + print("\n" + "=" * 80) + print("8. Cleanup") + print("=" * 80) + + cleanup = input("\nDelete created relationships and tables? (y/n): ").strip().lower() + + if cleanup == "y": + # Delete relationships first (required before deleting tables) + log_call("Deleting relationships") + try: + if rel_id_1: + await backoff(lambda: client.tables.delete_relationship(rel_id_1)) + print(f" [OK] Deleted relationship: new_Department_Employee") + except Exception as e: + print(f" [WARN] Error deleting relationship 1: {e}") + + try: + if rel_id_2: + await backoff(lambda: client.tables.delete_relationship(rel_id_2)) + print(f" [OK] Deleted relationship: contact->employee (Manager)") + except Exception as e: + print(f" [WARN] Error deleting relationship 2: {e}") + + try: + if rel_id_3: + await backoff(lambda: client.tables.delete_relationship(rel_id_3)) + print(f" [OK] Deleted relationship: new_employee_project") + except Exception as e: + print(f" [WARN] Error deleting relationship 3: {e}") + + # Delete tables + log_call("Deleting tables") + for table_name in ["new_Employee", "new_Department", "new_Project"]: + try: + await backoff(lambda name=table_name: client.tables.delete(name)) + print(f" [OK] Deleted table: {table_name}") + except Exception as e: + print(f" [WARN] Error deleting {table_name}: {e}") + + print("\n[OK] Cleanup complete") + else: + print("\nSkipping cleanup. Remember to manually delete:") + print(" - Relationships: new_Department_Employee, contact->employee (Manager), new_employee_project") + print(" - Tables: new_Employee, new_Department, new_Project") + + print("\n" + "=" * 80) + print("Example Complete!") + print("=" * 80) + + +if __name__ == "__main__": + try: + asyncio.run(main()) + except KeyboardInterrupt: + print("\n\nExample interrupted by user.") + sys.exit(1) + except Exception as e: + print(f"\n\nError: {e}") + import traceback + + traceback.print_exc() + sys.exit(1) diff --git a/examples/aio/advanced/sql_examples.py b/examples/aio/advanced/sql_examples.py new file mode 100644 index 00000000..4edd635c --- /dev/null +++ b/examples/aio/advanced/sql_examples.py @@ -0,0 +1,925 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +""" +Async end-to-end SQL query examples -- pure SQL workflows in Dataverse. + +Async equivalent of examples/advanced/sql_examples.py. + +This example demonstrates everything a SQL developer can do through the +Python SDK's ``client.query.sql()`` and ``client.dataframe.sql()`` methods. + +See examples/advanced/sql_examples.py for the complete capability reference. + +Prerequisites: +- pip install PowerPlatform-Dataverse-Client azure-identity +""" + +import asyncio +import sys +import json +from collections import defaultdict +from enum import IntEnum + +import pandas as pd +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parents[1])) +from _auth import AsyncInteractiveBrowserCredential +from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient +from PowerPlatform.Dataverse.core.errors import MetadataError + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + + +def log_call(description): + print(f"\n-> {description}") + + +def heading(section_num, title): + print(f"\n{'=' * 80}") + print(f"{section_num}. {title}") + print("=" * 80) + + +async def backoff(coro_fn, *, delays=(0, 2, 5, 10, 20, 20)): + """Retry an async operation with exponential back-off.""" + last = None + total_delay = 0 + attempts = 0 + for d in delays: + if d: + await asyncio.sleep(d) + total_delay += d + attempts += 1 + try: + result = await coro_fn() + if attempts > 1: + print(f" [INFO] Backoff succeeded after {attempts - 1} " f"retry(s); waited {total_delay}s total.") + return result + except Exception as ex: + last = ex + continue + if last: + if attempts: + print( + f" [WARN] Backoff exhausted after {max(attempts - 1, 0)} retry(s); waited {total_delay}s total." + f"\n [ERROR] {last}" + ) + raise last + + +class Region(IntEnum): + NORTH = 1 + SOUTH = 2 + EAST = 3 + WEST = 4 + + +async def main(): + print("=" * 80) + print("Dataverse SDK -- Async SQL End-to-End (Pure SQL Workflows)") + print("=" * 80) + + heading(1, "Setup & Authentication") + base_url = input("Enter Dataverse org URL (e.g. https://yourorg.crm.dynamics.com): ").strip() + if not base_url: + print("No URL entered; exiting.") + sys.exit(1) + base_url = base_url.rstrip("/") + + log_call("AsyncInteractiveBrowserCredential()") + credential = AsyncInteractiveBrowserCredential() + + log_call(f"AsyncDataverseClient(base_url='{base_url}', credential=...)") + try: + async with AsyncDataverseClient(base_url=base_url, credential=credential) as client: + print(f"[OK] Connected to: {base_url}") + await _run_examples(client) + finally: + await credential.close() + + +async def _run_examples(client): + parent_table = "new_SQLDemoTeam" + child_table = "new_SQLDemoTask" + + # ================================================================== + # 2. Seed demo data (SDK writes -- SQL is read-only) + # ================================================================== + heading(2, "Seed Demo Data (SDK Writes -- SQL Is Read-Only)") + print( + "[INFO] SQL is read-only (no INSERT/UPDATE/DELETE). We use the SDK's\n" + "records namespace to seed data, then query it all via SQL." + ) + + log_call(f"client.tables.get('{parent_table}')") + if await client.tables.get(parent_table): + print(f"[OK] Table already exists: {parent_table}") + else: + log_call(f"client.tables.create('{parent_table}', ...)") + try: + await backoff( + lambda: client.tables.create( + parent_table, + { + "new_Code": "string", + "new_Region": Region, + "new_Budget": "decimal", + "new_Active": "bool", + }, + ) + ) + print(f"[OK] Created table: {parent_table}") + except Exception as e: + if "already exists" in str(e).lower() or "not unique" in str(e).lower(): + print(f"[OK] Table already exists: {parent_table} (skipped)") + else: + raise + + log_call(f"client.tables.get('{child_table}')") + if await client.tables.get(child_table): + print(f"[OK] Table already exists: {child_table}") + else: + log_call(f"client.tables.create('{child_table}', ...)") + try: + await backoff( + lambda: client.tables.create( + child_table, + { + "new_Title": "string", + "new_Hours": "int", + "new_Done": "bool", + "new_Priority": "int", + }, + ) + ) + print(f"[OK] Created table: {child_table}") + except Exception as e: + if "already exists" in str(e).lower() or "not unique" in str(e).lower(): + print(f"[OK] Table already exists: {child_table} (skipped)") + else: + raise + + # Create lookup so tasks reference teams via JOIN + print("\n[INFO] Creating lookup field so tasks reference teams via JOIN...") + try: + await client.tables.create_lookup_field( + referencing_table=child_table, + lookup_field_name="new_TeamId", + referenced_table=parent_table, + display_name="Team", + ) + print("[OK] Created lookup: new_TeamId on tasks -> teams") + except Exception as e: + msg = str(e).lower() + if "already exists" in msg or "duplicate" in msg or "not unique" in msg: + print("[OK] Lookup already exists (skipped)") + else: + raise + + log_call(f"client.records.create('{parent_table}', [...])") + teams = [ + {"new_Code": "ALPHA", "new_Region": Region.NORTH, "new_Budget": 50000, "new_Active": True}, + {"new_Code": "BRAVO", "new_Region": Region.SOUTH, "new_Budget": 75000, "new_Active": True}, + {"new_Code": "CHARLIE", "new_Region": Region.EAST, "new_Budget": 30000, "new_Active": False}, + {"new_Code": "DELTA", "new_Region": Region.WEST, "new_Budget": 90000, "new_Active": True}, + {"new_Code": "ECHO", "new_Region": Region.NORTH, "new_Budget": 42000, "new_Active": True}, + ] + team_ids = await backoff(lambda: client.records.create(parent_table, teams)) + print(f"[OK] Seeded {len(team_ids)} teams") + + parent_logical = parent_table.lower() + parent_set = f"{parent_logical}s" + try: + tinfo = await client.tables.get(parent_table) + if tinfo: + parent_set = tinfo.get("entity_set_name", parent_set) + except Exception: + pass + + log_call(f"client.records.create('{child_table}', [...])") + tasks = [ + { + "new_Title": "Design mockups", + "new_Hours": 8, + "new_Done": True, + "new_Priority": 2, + f"new_TeamId@odata.bind": f"/{parent_set}({team_ids[0]})", + }, + { + "new_Title": "Write unit tests", + "new_Hours": 12, + "new_Done": False, + "new_Priority": 3, + f"new_TeamId@odata.bind": f"/{parent_set}({team_ids[0]})", + }, + { + "new_Title": "Code review", + "new_Hours": 3, + "new_Done": True, + "new_Priority": 1, + f"new_TeamId@odata.bind": f"/{parent_set}({team_ids[1]})", + }, + { + "new_Title": "Deploy to staging", + "new_Hours": 5, + "new_Done": False, + "new_Priority": 3, + f"new_TeamId@odata.bind": f"/{parent_set}({team_ids[1]})", + }, + { + "new_Title": "Update docs", + "new_Hours": 4, + "new_Done": True, + "new_Priority": 1, + f"new_TeamId@odata.bind": f"/{parent_set}({team_ids[2]})", + }, + { + "new_Title": "Performance tuning", + "new_Hours": 10, + "new_Done": False, + "new_Priority": 2, + f"new_TeamId@odata.bind": f"/{parent_set}({team_ids[3]})", + }, + { + "new_Title": "Security audit", + "new_Hours": 6, + "new_Done": False, + "new_Priority": 3, + f"new_TeamId@odata.bind": f"/{parent_set}({team_ids[4]})", + }, + ] + task_ids = await backoff(lambda: client.records.create(child_table, tasks)) + print(f"[OK] Seeded {len(task_ids)} tasks (with team lookups)") + + parent_id_col = f"{parent_logical}id" + + try: + # ============================================================== + # 3. Schema discovery + # ============================================================== + heading(3, "Schema Discovery Before Writing SQL") + log_call(f"client.tables.list_columns('{parent_table}', select=[...])") + columns = await backoff( + lambda: client.tables.list_columns( + parent_table, + select=["LogicalName", "SchemaName", "AttributeType"], + ) + ) + custom_cols = [c for c in columns if c.get("LogicalName", "").startswith("new_")] + print(f"[OK] Custom columns on {parent_table}:") + for col in custom_cols: + print(f" {col['LogicalName']:30s} Type: {col.get('AttributeType', 'N/A')}") + + log_call(f"client.tables.list_table_relationships('{child_table}', ...)") + rels = await backoff( + lambda: client.tables.list_table_relationships( + child_table, + select=["SchemaName"], + ) + ) + print(f"[OK] Relationships on {child_table}: {len(rels)}") + + # ============================================================== + # 4. Basic SELECT + # ============================================================== + heading(4, "Basic SQL -- SELECT Specific Columns") + sql = f"SELECT new_code, new_budget, new_active FROM {parent_table}" + log_call(f'client.query.sql("{sql}")') + results = await backoff(lambda: client.query.sql(sql)) + print(f"[OK] {len(results)} rows:") + for r in results: + print(f" {r.get('new_code', ''):<12s} Budget={r.get('new_budget')} Active={r.get('new_active')}") + + # ============================================================== + # 5. SELECT * -- Rejected by Design + # ============================================================== + heading(5, "SELECT * -- Rejected by Design") + print( + "SELECT * is deliberately rejected -- not a server workaround,\n" + "but an intentional design decision. Wide entities (e.g. account\n" + "has 307 columns) make SELECT * extremely expensive on shared\n" + "infrastructure. Specify columns explicitly instead.\n" + "Use client.query.sql_columns('account') to discover column names." + ) + from PowerPlatform.Dataverse.core.errors import ValidationError as _VE + + try: + await client.query.sql(f"SELECT * FROM {parent_table}") + print("[UNEXPECTED] SELECT * did not raise -- check SDK version") + except _VE as exc: + print(f"[OK] ValidationError raised as expected: {exc}") + + # ============================================================== + # 6. WHERE clause + # ============================================================== + heading(6, "SQL -- WHERE (=, >, <, IN, IS NULL, BETWEEN)") + sql = f"SELECT new_code, new_budget FROM {parent_table} WHERE new_budget > 40000" + log_call(f'client.query.sql("{sql}")') + results = await backoff(lambda: client.query.sql(sql)) + print(f"[OK] budget > 40000: {len(results)} rows") + + sql = f"SELECT new_code FROM {parent_table} WHERE new_code IN ('ALPHA', 'DELTA')" + results = await backoff(lambda: client.query.sql(sql)) + print(f"[OK] IN clause: {[r.get('new_code') for r in results]}") + + sql = f"SELECT new_title FROM {child_table} WHERE new_priority IS NOT NULL" + results = await backoff(lambda: client.query.sql(sql)) + print(f"[OK] IS NOT NULL: {len(results)} tasks") + + # ============================================================== + # 7. LIKE + # ============================================================== + heading(7, "SQL -- LIKE Pattern Matching") + sql = f"SELECT new_title FROM {child_table} WHERE new_title LIKE '%test%'" + log_call(f'client.query.sql("{sql}")') + results = await backoff(lambda: client.query.sql(sql)) + print(f"[OK] LIKE '%test%': {len(results)} matches") + + # ============================================================== + # 8. TOP + ORDER BY + # ============================================================== + heading(8, "SQL -- TOP N + ORDER BY") + sql = f"SELECT TOP 3 new_code, new_budget FROM {parent_table} ORDER BY new_budget DESC" + log_call(f'client.query.sql("{sql}")') + results = await backoff(lambda: client.query.sql(sql)) + print(f"[OK] Top 3 by budget:") + for r in results: + print(f" {r.get('new_code', ''):<12s} Budget={r.get('new_budget')}") + + # ============================================================== + # 9. Aliases + # ============================================================== + heading(9, "SQL -- Table and Column Aliases") + sql = ( + f"SELECT t.new_code AS team_code, t.new_budget AS budget " + f"FROM {parent_table} AS t WHERE t.new_active = 1" + ) + log_call(f'client.query.sql("{sql}")') + results = await backoff(lambda: client.query.sql(sql)) + print(f"[OK] Aliased results: {len(results)} rows") + + # ============================================================== + # 10. DISTINCT + # ============================================================== + heading(10, "SQL -- DISTINCT") + sql = f"SELECT DISTINCT new_region FROM {parent_table}" + log_call(f'client.query.sql("{sql}")') + results = await backoff(lambda: client.query.sql(sql)) + print(f"[OK] Distinct regions: {[r.get('new_region') for r in results]}") + + sql = f"SELECT DISTINCT TOP 2 new_region FROM {parent_table}" + results = await backoff(lambda: client.query.sql(sql)) + print(f"[OK] DISTINCT TOP 2: {[r.get('new_region') for r in results]}") + + # ============================================================== + # 11. Aggregates: COUNT, SUM, AVG, MIN, MAX + # ============================================================== + heading(11, "SQL -- Aggregates (All Run on Server)") + sql = ( + f"SELECT COUNT(*) as cnt, SUM(new_budget) as total, " + f"AVG(new_budget) as avg_b, MIN(new_budget) as min_b, " + f"MAX(new_budget) as max_b FROM {parent_table}" + ) + log_call('client.query.sql("SELECT COUNT, SUM, AVG, MIN, MAX...")') + results = await backoff(lambda: client.query.sql(sql)) + if results: + print(f"[OK] {json.dumps(dict(results[0]), indent=2)}") + + # ============================================================== + # 12. GROUP BY + # ============================================================== + heading(12, "SQL -- GROUP BY (Server-Side)") + sql = ( + f"SELECT new_region, COUNT(*) as team_count, " + f"SUM(new_budget) as total_budget " + f"FROM {parent_table} GROUP BY new_region" + ) + log_call(f'client.query.sql("{sql}")') + results = await backoff(lambda: client.query.sql(sql)) + print(f"[OK] {len(results)} groups:") + for r in results: + print(f" Region={r.get('new_region')} Count={r.get('team_count')} Total={r.get('total_budget')}") + + # ============================================================== + # 13. INNER JOIN + # ============================================================== + heading(13, "SQL -- INNER JOIN") + print("Use the lookup attribute's logical name (e.g. new_teamid) for JOINs.") + + lookup_col = "new_teamid" # Lookup logical name, NOT _..._value + join_clause = f"JOIN {parent_table} t ON tk.{lookup_col} = t.{parent_logical}id" + print(f"[INFO] Lookup column: {lookup_col}") + print(f"[INFO] JOIN clause: {join_clause}") + + sql = f"SELECT t.new_code, tk.new_title, tk.new_hours FROM {child_table} tk {join_clause}" + log_call('client.query.sql("...INNER JOIN...")') + try: + results = await backoff(lambda: client.query.sql(sql)) + print(f"[OK] JOIN: {len(results)} rows") + for r in results[:5]: + print( + f" Team={r.get('new_code', ''):<10s} Task={r.get('new_title', ''):<25s} Hours={r.get('new_hours')}" + ) + except Exception as e: + print(f"[WARN] JOIN failed: {e}") + + # ============================================================== + # 14. LEFT JOIN + # ============================================================== + heading(14, "SQL -- LEFT JOIN") + sql = ( + f"SELECT t.new_code, tk.new_title " + f"FROM {parent_table} t " + f"LEFT JOIN {child_table} tk ON t.{parent_id_col} = tk.{lookup_col}" + ) + log_call('client.query.sql("...LEFT JOIN...")') + try: + results = await backoff(lambda: client.query.sql(sql)) + print(f"[OK] LEFT JOIN: {len(results)} rows") + except Exception as e: + print(f"[WARN] LEFT JOIN failed: {e}") + + # ============================================================== + # 15. JOIN + GROUP BY + aggregates + # ============================================================== + heading(15, "SQL -- JOIN + GROUP BY + Aggregates") + sql = ( + f"SELECT t.new_code, COUNT(tk.new_sqldemotaskid) as task_count, " + f"SUM(tk.new_hours) as total_hours " + f"FROM {parent_table} t " + f"JOIN {child_table} tk ON t.{parent_id_col} = tk.{lookup_col} " + f"GROUP BY t.new_code" + ) + log_call('client.query.sql("...JOIN...GROUP BY...")') + try: + results = await backoff(lambda: client.query.sql(sql)) + print(f"[OK] {len(results)} groups:") + for r in results: + print(f" Team={r.get('new_code', ''):<10s} Tasks={r.get('task_count')} Hours={r.get('total_hours')}") + except Exception as e: + print(f"[WARN] JOIN+GROUP BY failed: {e}") + + # ============================================================== + # 16. OFFSET FETCH (server-side pagination) + # ============================================================== + heading(16, "SQL -- OFFSET FETCH (Server-Side Pagination)") + page_size = 3 + for pg in range(1, 4): + offset = (pg - 1) * page_size + sql = ( + f"SELECT new_title, new_hours FROM {child_table} " + f"ORDER BY new_hours " + f"OFFSET {offset} ROWS FETCH NEXT {page_size} ROWS ONLY" + ) + log_call(f"Page {pg}: OFFSET {offset} FETCH NEXT {page_size}") + results = await backoff(lambda sql=sql: client.query.sql(sql)) + print(f" Page {pg}: {len(results)} rows") + for r in results: + print(f" {r.get('new_title', ''):<25s} Hours={r.get('new_hours')}") + if len(results) < page_size: + break + + # ============================================================== + # 17. SQL to DataFrame + # ============================================================== + heading(17, "SQL to DataFrame (client.dataframe.sql)") + print("Get SQL results directly as a pandas DataFrame.") + sql = f"SELECT new_code, new_budget, new_region " f"FROM {parent_table} ORDER BY new_budget DESC" + log_call(f'client.dataframe.sql("{sql}")') + df = await backoff(lambda: client.dataframe.sql(sql)) + print(f"[OK] DataFrame: {len(df)} rows x {len(df.columns)} columns") + print(df.to_string(index=False)) + print(f"\n Mean budget: {df['new_budget'].mean():,.2f}") + print(f" Budget by region:\n{df.groupby('new_region')['new_budget'].sum()}") + + # ============================================================== + # 18. SQL to DataFrame with JOINs + # ============================================================== + heading(18, "SQL to DataFrame -- JOIN Query") + sql = ( + f"SELECT t.new_code, tk.new_title, tk.new_hours " + f"FROM {child_table} tk " + f"JOIN {parent_table} t ON tk.{lookup_col} = t.{parent_id_col}" + ) + log_call('client.dataframe.sql("...JOIN...")') + try: + df_j = await backoff(lambda: client.dataframe.sql(sql)) + print(f"[OK] {len(df_j)} rows") + print(df_j.to_string(index=False)) + print("\n-- Pivot: hours by team --") + print(df_j.groupby("new_code")["new_hours"].agg(["sum", "mean", "count"]).to_string()) + except Exception as e: + print(f"[WARN] {e}") + + # ============================================================== + # 19. Built-in table JOINs + # ============================================================== + heading(19, "Built-In Table JOINs (account -> contact)") + sql = "SELECT a.name, c.fullname FROM account a " "INNER JOIN contact c ON a.accountid = c.parentcustomerid" + log_call('client.query.sql("...account JOIN contact...")') + try: + results = await backoff(lambda: client.query.sql(sql)) + print(f"[OK] {len(results)} rows") + for r in results[:5]: + print(f" Account={r.get('name', ''):<25s} Contact={r.get('fullname', '')}") + except Exception as e: + print(f"[INFO] {e}") + + # ============================================================== + # 20. LIMITATION: Writes require SDK + # ============================================================== + heading(20, "LIMITATION: Writes Require SDK (Read-Only SQL)") + sql = f"SELECT new_sqldemotaskid, new_title " f"FROM {child_table} WHERE new_done = 0" + incomplete = await backoff(lambda: client.query.sql(sql)) + print(f"[OK] SQL found {len(incomplete)} incomplete tasks") + if incomplete: + fid = incomplete[0].get("new_sqldemotaskid") + if fid: + await backoff(lambda: client.records.update(child_table, fid, {"new_Done": True})) + print(f"[OK] Updated via SDK: '{incomplete[0].get('new_title')}'") + + # ============================================================== + # 21. LIMITATION: No subqueries + # ============================================================== + heading(21, "LIMITATION: No Subqueries -- Chain SQL Calls") + sql1 = f"SELECT {parent_id_col} FROM {parent_table} WHERE new_budget > 50000" + big = await backoff(lambda: client.query.sql(sql1)) + big_ids = [r.get(parent_id_col) for r in big if r.get(parent_id_col)] + print(f"[OK] Step 1: {len(big_ids)} teams with budget > 50000") + if big_ids: + id_list = ", ".join(f"'{i}'" for i in big_ids) + sql2 = f"SELECT new_title FROM {child_table} " f"WHERE {lookup_col} IN ({id_list})" + tasks_r = await backoff(lambda: client.query.sql(sql2)) + print(f"[OK] Step 2: {len(tasks_r)} tasks for big-budget teams") + + # ============================================================== + # 22. LIMITATION: No functions + # ============================================================== + heading(22, "LIMITATION: No Functions -- Post-Process in Python") + sql = f"SELECT new_code, new_budget FROM {parent_table}" + rows = await backoff(lambda: client.query.sql(sql)) + print("[OK] Post-processing (CASE equivalent):") + for r in rows: + b = float(r.get("new_budget") or 0) + tier = "HIGH" if b > 60000 else "MEDIUM" if b > 35000 else "LOW" + print(f" {r.get('new_code', ''):<12s} Budget={b:>10,.2f} Tier={tier}") + + # ============================================================== + # 23. Polymorphic lookups via SQL (ownerid, customerid) + # ============================================================== + heading(23, "Polymorphic Lookups via SQL (ownerid, customerid)") + print( + "Some Dataverse lookup columns are POLYMORPHIC -- the GUID can\n" + "point to different entity types (e.g. ownerid -> systemuser OR\n" + "team, customerid -> account OR contact).\n" + "\n" + "SQL pattern: INNER JOIN acts as both a join AND a type filter.\n" + "If the GUID points to a different type, the JOIN simply returns\n" + "no row -- so you get exactly the records of the type you joined." + ) + + # 23a. Discover lookup columns on a table + print("\n-- 23a. Discover lookup columns on account --") + log_call("client.tables.list_columns('account', filter=Lookup)") + try: + acct_cols = await backoff( + lambda: client.tables.list_columns( + "account", + select=["LogicalName", "AttributeType"], + filter="AttributeType eq 'Lookup' or AttributeType eq 'Owner' or AttributeType eq 'Customer'", + ) + ) + lookup_names = sorted(c.get("LogicalName", "") for c in acct_cols if c.get("LogicalName", "")) + print(f"[OK] Lookup columns on account ({len(lookup_names)} found):") + for ln in lookup_names[:10]: + print(f" {ln}") + if len(lookup_names) > 10: + print(f" ... and {len(lookup_names) - 10} more") + except Exception as e: + print(f"[INFO] Lookup discovery skipped: {e}") + + # 23b. Discover polymorphic targets via relationship metadata + print("\n-- 23b. Discover which entities a polymorphic lookup targets --") + log_call("client.tables.list_table_relationships('account', ...)") + try: + acct_rels = await backoff(lambda: client.tables.list_table_relationships("account")) + by_attr = defaultdict(list) + for rel in acct_rels: + attr = rel.get("ReferencingAttribute", "") + ref = rel.get("ReferencedEntity", "") + if attr and ref and rel.get("ReferencingEntity", "").lower() == "account": + by_attr[attr].append(ref) + print("[OK] Lookup targets on account:") + for attr, targets in sorted(by_attr.items()): + tag = "POLYMORPHIC" if len(targets) > 1 else "regular" + print(f" {attr:<35s} -> {', '.join(targets):<30s} [{tag}]") + except Exception as e: + print(f"[INFO] Relationship discovery skipped: {e}") + + # 23c. Resolve ownerid (polymorphic: systemuser or team) + print("\n-- 23c. Resolve ownerid via SQL JOINs --") + print("ownerid is polymorphic (systemuser or team). Use separate\n" "JOINs and combine in a DataFrame.") + try: + # Records owned by users + log_call("SQL: account JOIN systemuser ON ownerid") + df_user_owned = await backoff( + lambda: client.dataframe.sql( + "SELECT TOP 5 a.name, su.fullname as owner_name " + "FROM account a " + "INNER JOIN systemuser su ON a.ownerid = su.systemuserid" + ) + ) + df_user_owned["owner_type"] = "User" + + # Records owned by teams + log_call("SQL: account JOIN team ON ownerid") + df_team_owned = await backoff( + lambda: client.dataframe.sql( + "SELECT TOP 5 a.name, t.name as owner_name " + "FROM account a " + "INNER JOIN team t ON a.ownerid = t.teamid" + ) + ) + df_team_owned["owner_type"] = "Team" + + df_owners = pd.concat([df_user_owned, df_team_owned], ignore_index=True) + print(f"[OK] Owner resolution: {len(df_owners)} rows") + print(f" User-owned: {len(df_user_owned)}") + print(f" Team-owned: {len(df_team_owned)}") + if not df_owners.empty: + print(df_owners.to_string(index=False)) + except Exception as e: + print(f"[INFO] Owner resolution skipped (may have no data): {e}") + + # 23d. Track created-by and modified-by (common audit pattern) + print("\n-- 23d. Audit trail: who created/modified records (via SQL) --") + try: + log_call("SQL: account JOIN systemuser (createdby + modifiedby)") + results = await backoff( + lambda: client.query.sql( + "SELECT TOP 5 a.name, " + "creator.fullname as created_by, " + "modifier.fullname as modified_by " + "FROM account a " + "JOIN systemuser creator ON a.createdby = creator.systemuserid " + "JOIN systemuser modifier ON a.modifiedby = modifier.systemuserid" + ) + ) + print(f"[OK] Audit trail: {len(results)} rows") + for r in results[:5]: + print( + f" {r.get('name', ''):<25s} " + f"Created: {r.get('created_by', ''):<20s} " + f"Modified: {r.get('modified_by', '')}" + ) + except Exception as e: + print(f"[INFO] Audit trail skipped: {e}") + + # ============================================================== + # 24. SQL Read -> DataFrame Transform -> SDK Write-Back + # ============================================================== + heading(24, "SQL Read -> DataFrame Transform -> SDK Write-Back") + print( + "The full bidirectional workflow for SQL users:\n" + " 1. SQL query -> DataFrame (read)\n" + " 2. pandas -> Transform (compute)\n" + " 3. DataFrame -> SDK write-back (create/update/delete)\n" + "\n" + "This is how SQL developers do end-to-end work without\n" + "learning OData or the Web API." + ) + + # Read current state via SQL + sql = f"SELECT new_sqldemotaskid, new_title, new_hours, new_done " f"FROM {child_table}" + log_call(f'client.dataframe.sql("{sql}")') + df_tasks = await backoff(lambda: client.dataframe.sql(sql)) + print(f"[OK] Read {len(df_tasks)} tasks via SQL") + print(df_tasks.to_string(index=False)) + + # Transform: bump hours by 1 for incomplete tasks + mask = df_tasks["new_done"] == False # noqa: E712 + original_hours = df_tasks.loc[mask, "new_hours"].copy() + df_tasks.loc[mask, "new_hours"] = df_tasks.loc[mask, "new_hours"] + 1 + changed = mask.sum() + print(f"\n[OK] Bumped hours +1 for {changed} incomplete tasks (in DataFrame)") + + # Write back via SDK + if changed > 0: + updates = df_tasks.loc[mask, ["new_sqldemotaskid", "new_hours"]] + log_call(f"client.dataframe.update('{child_table}', ..., id_column='new_sqldemotaskid')") + await backoff(lambda: client.dataframe.update(child_table, updates, id_column="new_sqldemotaskid")) + print(f"[OK] Wrote back {len(updates)} updated rows via DataFrame") + + # Verify with SQL + verify = await backoff( + lambda: client.dataframe.sql(f"SELECT new_title, new_hours FROM {child_table} WHERE new_done = 0") + ) + print(f"[OK] Verified via SQL -- incomplete tasks now:") + print(verify.to_string(index=False)) + + # Restore original values + df_tasks.loc[mask, "new_hours"] = original_hours + restore = df_tasks.loc[mask, ["new_sqldemotaskid", "new_hours"]] + await backoff(lambda: client.dataframe.update(child_table, restore, id_column="new_sqldemotaskid")) + print("[OK] Restored original hours") + + # ============================================================== + # 25. SQL-driven bulk create from query results + # ============================================================== + heading(25, "SQL-Driven Bulk Create (Query -> Transform -> Insert)") + print( + "Pattern: query existing data with SQL, transform it,\n" + "then create new records via DataFrame -- all without\n" + "learning OData syntax." + ) + + # Read teams via SQL + sql = f"SELECT new_code, new_budget FROM {parent_table} WHERE new_active = 1" + log_call(f'client.dataframe.sql("{sql}")') + df_active = await backoff(lambda: client.dataframe.sql(sql)) + print(f"[OK] Read {len(df_active)} active teams via SQL") + + # Transform: create a new task for each active team + new_tasks = pd.DataFrame( + { + "new_Title": [f"Review budget for {code}" for code in df_active["new_code"]], + "new_Hours": [2] * len(df_active), + "new_Done": [False] * len(df_active), + "new_Priority": [1] * len(df_active), + } + ) + log_call(f"client.dataframe.create('{child_table}', DataFrame({len(new_tasks)} rows))") + new_ids = await backoff(lambda: client.dataframe.create(child_table, new_tasks)) + print(f"[OK] Created {len(new_ids)} new tasks from SQL query results") + + # Verify with SQL + verify_sql = f"SELECT new_title, new_hours FROM {child_table} " f"WHERE new_title LIKE 'Review budget%'" + created_tasks = await backoff(lambda: client.query.sql(verify_sql)) + print(f"[OK] Verified via SQL: {len(created_tasks)} 'Review budget' tasks") + + # Clean up the created tasks + await backoff(lambda: client.dataframe.delete(child_table, new_ids)) + print(f"[OK] Cleaned up {len(new_ids)} demo tasks") + + # ============================================================== + # 26. SQL-driven bulk delete + # ============================================================== + heading(26, "SQL-Driven Bulk Delete (Query -> Filter -> Delete)") + print("Pattern: find records with SQL, filter in pandas,\n" "then delete via DataFrame -- pure SQL thinking.") + + # Create some temp records to demonstrate + temp = pd.DataFrame( + { + "new_Title": ["TEMP: delete me 1", "TEMP: delete me 2", "TEMP: keep me"], + "new_Hours": [1, 2, 3], + "new_Done": [False, False, False], + "new_Priority": [1, 1, 1], + } + ) + temp_ids = await backoff(lambda: client.dataframe.create(child_table, temp)) + print(f"[OK] Created {len(temp_ids)} temp records") + + # SQL to find, pandas to filter, SDK to delete + sql = f"SELECT new_sqldemotaskid, new_title FROM {child_table} WHERE new_title LIKE 'TEMP:%'" + df_temp = await backoff(lambda: client.dataframe.sql(sql)) + print(f"[OK] SQL found {len(df_temp)} TEMP records") + + # Filter in pandas: only delete the "delete me" ones + to_delete = df_temp[df_temp["new_title"].str.contains("delete me")] + print(f"[OK] Pandas filtered to {len(to_delete)} records to delete") + + if not to_delete.empty: + log_call("client.dataframe.delete(...)") + await backoff(lambda: client.dataframe.delete(child_table, to_delete["new_sqldemotaskid"])) + print(f"[OK] Deleted {len(to_delete)} records via DataFrame") + + # Verify the "keep me" record survived + remaining = await backoff( + lambda: client.query.sql(f"SELECT new_title FROM {child_table} WHERE new_title LIKE 'TEMP:%'") + ) + print(f"[OK] Remaining TEMP records: {len(remaining)}") + for r in remaining: + print(f" {r.get('new_title')}") + + # Clean up the surviving temp record + keep_ids = [ + r.get("new_sqldemotaskid") + for r in await backoff( + lambda: client.query.sql(f"SELECT new_sqldemotaskid FROM {child_table} WHERE new_title LIKE 'TEMP:%'") + ) + if r.get("new_sqldemotaskid") + ] + for kid in keep_ids: + await backoff(lambda kid=kid: client.records.delete(child_table, kid)) + + # ============================================================== + # 27. AND/OR, NOT IN, NOT LIKE + # ============================================================== + heading(27, "SQL -- AND/OR, NOT IN, NOT LIKE") + sql = f"SELECT new_code, new_budget FROM {parent_table} " f"WHERE new_active = 1 AND new_budget > 40000" + log_call(f'client.query.sql("{sql}")') + results = await backoff(lambda: client.query.sql(sql)) + print(f"[OK] AND: {len(results)} rows") + + sql = f"SELECT new_code FROM {parent_table} " f"WHERE new_code = 'ALPHA' OR new_code = 'DELTA'" + results = await backoff(lambda: client.query.sql(sql)) + print(f"[OK] OR: {[r.get('new_code') for r in results]}") + + sql = ( + f"SELECT new_code FROM {parent_table} " + f"WHERE new_active = 1 AND (new_budget > 80000 OR new_budget < 45000)" + ) + results = await backoff(lambda: client.query.sql(sql)) + print(f"[OK] AND + OR with parens: {len(results)} rows") + + sql = f"SELECT new_code FROM {parent_table} WHERE new_code NOT IN ('ALPHA')" + results = await backoff(lambda: client.query.sql(sql)) + print(f"[OK] NOT IN: {[r.get('new_code') for r in results]}") + + sql = f"SELECT new_title FROM {child_table} WHERE new_title NOT LIKE 'Design%'" + results = await backoff(lambda: client.query.sql(sql)) + print(f"[OK] NOT LIKE: {len(results)} rows") + + # ============================================================== + # 28. Deep JOINs (5-8 tables) + # ============================================================== + heading(28, "Deep JOINs (5+ Tables) -- No Depth Limit") + + sql = ( + "SELECT TOP 3 a.name, c.fullname, o.name as opp, " + "su.fullname as owner, bu.name as bu " + "FROM account a " + "JOIN contact c ON a.accountid = c.parentcustomerid " + "JOIN opportunity o ON a.accountid = o.parentaccountid " + "JOIN systemuser su ON a.ownerid = su.systemuserid " + "JOIN businessunit bu ON su.businessunitid = bu.businessunitid" + ) + log_call("5-table JOIN") + try: + results = await backoff(lambda: client.query.sql(sql)) + print(f"[OK] 5-table JOIN: {len(results)} rows") + except Exception as e: + print(f"[INFO] {e}") + + # ============================================================== + # 29. SQL Helper Functions + # ============================================================== + heading(29, "SQL Helper Functions (query.sql_*)") + print( + "At GA, sql_columns() is the only retained SQL schema-discovery helper.\n" + "sql_select(), sql_join(), and sql_joins() were removed -- write JOIN\n" + "clauses directly or use client.query.fetchxml() for complex queries." + ) + + # sql_columns — still available at GA + log_call(f"client.query.sql_columns('{parent_table}')") + cols = await client.query.sql_columns(parent_table) + print(f"[OK] {len(cols)} columns:") + for c in cols[:5]: + print(f" {c['name']:30s} Type: {c['type']:15s} PK={c['is_pk']}") + + # ============================================================== + # 30. OData Helper Functions + # ============================================================== + heading(30, "OData Helper Functions (query.odata_expands)") + print( + "odata_expands() is available without deprecation.\n" + "odata_select(), odata_expand(), and odata_bind() are deprecated\n" + "at GA -- use the typed query builder instead." + ) + + # odata_expands + log_call(f"client.query.odata_expands('{child_table}')") + try: + expands = await client.query.odata_expands(child_table) + print(f"[OK] {len(expands)} expand targets:") + for e in expands[:5]: + print(f" nav={e['nav_property']:30s} -> {e['target_table']}") + except Exception as e: + print(f"[WARN] {e}") + + finally: + heading(31, "Cleanup") + for tbl in [child_table, parent_table]: + log_call(f"client.tables.delete('{tbl}')") + try: + await backoff(lambda tbl=tbl: client.tables.delete(tbl)) + print(f"[OK] Deleted table: {tbl}") + except Exception as ex: + code = getattr(getattr(ex, "response", None), "status_code", None) + if isinstance(ex, MetadataError) and code == 404: + print(f"[OK] Table already removed: {tbl}") + else: + print(f"[WARN] Could not delete {tbl}: {ex}") + + print("\n" + "=" * 80) + print("Async SQL Examples Complete!") + print("=" * 80) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/aio/advanced/walkthrough.py b/examples/aio/advanced/walkthrough.py new file mode 100644 index 00000000..d7a14e4a --- /dev/null +++ b/examples/aio/advanced/walkthrough.py @@ -0,0 +1,632 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +""" +Async walkthrough demonstrating core Dataverse SDK operations. + +Async equivalent of examples/advanced/walkthrough.py. + +This example shows: +- Table creation with various column types including enums +- Single and multiple record CRUD operations +- Querying with filtering, paging, AsyncQueryBuilder, and SQL +- Expand (navigation properties) with AsyncQueryBuilder +- Picklist label-to-value conversion +- Column management +- Batch operations (create, read, update, changeset, delete in one HTTP request) +- Cleanup + +Prerequisites: +- pip install PowerPlatform-Dataverse-Client +- pip install azure-identity +""" + +import asyncio +import sys +import json +from enum import IntEnum + +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parents[1])) +from _auth import AsyncInteractiveBrowserCredential +from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient +from PowerPlatform.Dataverse.core.errors import MetadataError +from PowerPlatform.Dataverse.models.filters import col +from PowerPlatform.Dataverse.models.query_builder import ExpandOption + + +def log_call(description): + print(f"\n-> {description}") + + +class Priority(IntEnum): + LOW = 1 + MEDIUM = 2 + HIGH = 3 + + +async def backoff(coro_fn, *, delays=(0, 2, 5, 10, 20, 20)): + """Retry a coroutine with exponential back-off for metadata propagation delays.""" + last = None + total_delay = 0 + attempts = 0 + for d in delays: + if d: + await asyncio.sleep(d) + total_delay += d + attempts += 1 + try: + result = await coro_fn() + if attempts > 1: + print(f" [INFO] Backoff succeeded after {attempts - 1} retry(s); waited {total_delay}s total.") + return result + except Exception as ex: + last = ex + continue + if last: + if attempts: + print(f" [WARN] Backoff exhausted after {max(attempts - 1, 0)} retry(s); waited {total_delay}s total.") + raise last + + +async def main(): + print("=" * 80) + print("Dataverse SDK Async Walkthrough") + print("=" * 80) + + # ============================================================================ + # 1. SETUP & AUTHENTICATION + # ============================================================================ + print("\n" + "=" * 80) + print("1. Setup & Authentication") + print("=" * 80) + + base_url = input("Enter Dataverse org URL (e.g. https://yourorg.crm.dynamics.com): ").strip() + if not base_url: + print("No URL entered; exiting.") + sys.exit(1) + + base_url = base_url.rstrip("/") + + log_call("AsyncInteractiveBrowserCredential()") + credential = AsyncInteractiveBrowserCredential() + + log_call(f"AsyncDataverseClient(base_url='{base_url}', credential=...)") + try: + async with AsyncDataverseClient(base_url=base_url, credential=credential) as client: + print(f"[OK] Connected to: {base_url}") + await _run_walkthrough(client) + finally: + await credential.close() + + +async def _run_walkthrough(client): + # ============================================================================ + # 2. TABLE CREATION (METADATA) + # ============================================================================ + print("\n" + "=" * 80) + print("2. Table Creation (Metadata)") + print("=" * 80) + + table_name = "new_WalkthroughDemo" + + log_call(f"await client.tables.get('{table_name}')") + table_info = await backoff(lambda: client.tables.get(table_name)) + + if table_info: + print(f"[OK] Table already exists: {table_info.get('table_schema_name')}") + print(f" Logical Name: {table_info.get('table_logical_name')}") + print(f" Entity Set: {table_info.get('entity_set_name')}") + else: + log_call(f"await client.tables.create('{table_name}', columns={{...}}, display_name='Walkthrough Demo')") + columns = { + "new_Title": "string", + "new_Quantity": "int", + "new_Amount": "decimal", + "new_Completed": "bool", + "new_Notes": "memo", + "new_Priority": Priority, + } + table_info = await backoff(lambda: client.tables.create(table_name, columns, display_name="Walkthrough Demo")) + print(f"[OK] Created table: {table_info.get('table_schema_name')}") + print(f" Columns created: {', '.join(table_info.get('columns_created', []))}") + + # ============================================================================ + # 3. CREATE OPERATIONS + # ============================================================================ + print("\n" + "=" * 80) + print("3. Create Operations") + print("=" * 80) + + log_call(f"await client.records.create('{table_name}', {{...}})") + single_record = { + "new_Title": "Complete project documentation", + "new_Quantity": 5, + "new_Amount": 1250.50, + "new_Completed": False, + "new_Notes": "This is a multiline memo field.\nIt supports longer text content.", + "new_Priority": Priority.MEDIUM, + } + id1 = await backoff(lambda: client.records.create(table_name, single_record)) + print(f"[OK] Created single record: {id1}") + + log_call(f"await client.records.create('{table_name}', [{{...}}, {{...}}, {{...}}])") + multiple_records = [ + { + "new_Title": "Review code changes", + "new_Quantity": 10, + "new_Amount": 500.00, + "new_Completed": True, + "new_Priority": Priority.HIGH, + }, + { + "new_Title": "Update test cases", + "new_Quantity": 8, + "new_Amount": 750.25, + "new_Completed": False, + "new_Priority": Priority.LOW, + }, + { + "new_Title": "Deploy to staging", + "new_Quantity": 3, + "new_Amount": 2000.00, + "new_Completed": False, + "new_Priority": Priority.HIGH, + }, + ] + ids = await backoff(lambda: client.records.create(table_name, multiple_records)) + print(f"[OK] Created {len(ids)} records: {ids}") + + # ============================================================================ + # 4. READ OPERATIONS + # ============================================================================ + print("\n" + "=" * 80) + print("4. Read Operations") + print("=" * 80) + + log_call(f"await client.records.retrieve('{table_name}', '{id1}')") + record = await backoff(lambda: client.records.retrieve(table_name, id1)) + print("[OK] Retrieved single record:") + print( + json.dumps( + { + "new_walkthroughdemoid": record.get("new_walkthroughdemoid"), + "new_title": record.get("new_title"), + "new_quantity": record.get("new_quantity"), + "new_amount": record.get("new_amount"), + "new_completed": record.get("new_completed"), + "new_notes": record.get("new_notes"), + "new_priority": record.get("new_priority"), + "new_priority@FormattedValue": record.get("new_priority@OData.Community.Display.V1.FormattedValue"), + }, + indent=2, + ) + ) + + log_call(f"await client.records.list('{table_name}', filter='new_quantity gt 5')") + all_records = await backoff(lambda: client.records.list(table_name, filter="new_quantity gt 5")) + print(f"[OK] Found {len(all_records)} records with new_quantity > 5") + for rec in all_records: + print(f" - new_Title='{rec.get('new_title')}', new_Quantity={rec.get('new_quantity')}") + + # ============================================================================ + # 5. UPDATE OPERATIONS + # ============================================================================ + print("\n" + "=" * 80) + print("5. Update Operations") + print("=" * 80) + + log_call(f"await client.records.update('{table_name}', '{id1}', {{...}})") + await backoff( + lambda: client.records.update( + table_name, + id1, + { + "new_Quantity": 100, + "new_Notes": "Updated memo field.\nNow with revised content across multiple lines.", + }, + ) + ) + updated = await backoff(lambda: client.records.retrieve(table_name, id1)) + print(f"[OK] Updated single record new_Quantity: {updated.get('new_quantity')}") + print(f" new_Notes: {repr(updated.get('new_notes'))}") + + log_call(f"await client.records.update('{table_name}', [{len(ids)} IDs], {{...}})") + await backoff(lambda: client.records.update(table_name, ids, {"new_Completed": True})) + print(f"[OK] Updated {len(ids)} records to new_Completed=True") + + # ============================================================================ + # 6. PAGING DEMO + # ============================================================================ + print("\n" + "=" * 80) + print("6. Paging Demo") + print("=" * 80) + + log_call(f"await client.records.create('{table_name}', [20 records])") + paging_records = [ + { + "new_Title": f"Paging test item {i}", + "new_Quantity": i, + "new_Amount": i * 10.0, + "new_Completed": False, + "new_Priority": Priority.LOW, + } + for i in range(1, 21) + ] + paging_ids = await backoff(lambda: client.records.create(table_name, paging_records)) + print(f"[OK] Created {len(paging_ids)} records for paging demo") + + log_call(f"async for page in client.query.builder('{table_name}').order_by().page_size(5).execute_pages()") + print("Fetching records with page_size=5...") + page_num = 0 + async for page in client.query.builder(table_name).order_by("new_Quantity").page_size(5).execute_pages(): + page_num += 1 + record_ids = [r.get("new_walkthroughdemoid")[:8] + "..." for r in page] + print(f" Page {page_num}: {len(page)} records - IDs: {record_ids}") + + # ============================================================================ + # 7. QUERYBUILDER - FLUENT QUERIES + # ============================================================================ + print("\n" + "=" * 80) + print("7. AsyncQueryBuilder - Fluent Queries") + print("=" * 80) + + log_call("await client.query.builder(...).select().where(col(...)==...).order_by().execute()") + print("Querying incomplete records ordered by amount (fluent builder)...") + qb_result = await backoff( + lambda: client.query.builder(table_name) + .select("new_Title", "new_Amount", "new_Priority") + .where(col("new_Completed") == False) + .order_by("new_Amount", descending=True) + .top(10) + .execute() + ) + print(f"[OK] AsyncQueryBuilder found {len(qb_result)} incomplete records:") + for rec in list(qb_result)[:5]: + print(f" - '{rec.get('new_title')}' Amount={rec.get('new_amount')}") + + log_call("await client.query.builder(...).where(col('new_Priority').in_([HIGH, LOW])).execute()") + print("Querying records with HIGH or LOW priority (col().in_())...") + priority_result = await backoff( + lambda: client.query.builder(table_name) + .select("new_Title", "new_Priority") + .where(col("new_Priority").in_([Priority.HIGH, Priority.LOW])) + .execute() + ) + print(f"[OK] Found {len(priority_result)} records with HIGH or LOW priority") + + log_call("await client.query.builder(...).where(col('new_Amount').between(500, 1500)).execute()") + range_result = await backoff( + lambda: client.query.builder(table_name) + .select("new_Title", "new_Amount") + .where(col("new_Amount").between(500, 1500)) + .execute() + ) + print(f"[OK] Found {len(range_result)} records with amount in [500, 1500]") + + log_call("await client.query.builder(...).where((col(...)==...) & (col(...) > ...)).execute()") + expr_result = await backoff( + lambda: client.query.builder(table_name) + .select("new_Title", "new_Amount", "new_Quantity") + .where((col("new_Completed") == False) & (col("new_Amount") > 100)) + .order_by("new_Amount", descending=True) + .top(5) + .execute() + ) + print(f"[OK] Expression tree query found {len(expr_result)} records:") + for rec in expr_result: + print(f" - '{rec.get('new_title')}' Amount={rec.get('new_amount')} Qty={rec.get('new_quantity')}") + + log_call("async for page in client.query.builder(...).where(...).page_size().execute_pages()") + print("Querying with combined expression filters and paging...") + combined_page_count = 0 + combined_record_count = 0 + async for page in ( + client.query.builder(table_name) + .select("new_Title", "new_Quantity") + .where(col("new_Completed") == False) + .where(col("new_Quantity").between(1, 15)) + .order_by("new_Quantity") + .page_size(3) + .execute_pages() + ): + combined_page_count += 1 + combined_record_count += len(page) + titles = [r.get("new_title", "?") for r in page] + print(f" Page {combined_page_count}: {len(page)} records - {titles}") + print(f"[OK] Combined query: {combined_record_count} records across {combined_page_count} page(s)") + + log_call(f"(await client.query.builder('{table_name}').select(...).where(...).execute()).to_dataframe()") + print("Querying completed records as a pandas DataFrame (to_dataframe)...") + completed_result = await backoff( + lambda: client.query.builder(table_name) + .select("new_title", "new_quantity") + .where(col("new_completed") == True) + .execute() + ) + df = completed_result.to_dataframe() + print(f"[OK] to_dataframe() returned {len(df)} rows, columns: {list(df.columns)}") + if not df.empty: + print(f" First row: new_title='{df.iloc[0].get('new_title')}', new_quantity={df.iloc[0].get('new_quantity')}") + print(f" Sum of new_quantity: {df['new_quantity'].sum()}") + else: + print(" (empty DataFrame)") + + # ============================================================================ + # 8. EXPAND (NAVIGATION PROPERTIES) + # ============================================================================ + print("\n" + "=" * 80) + print("8. Expand (Navigation Properties)") + print("=" * 80) + + log_call("await client.query.builder('account').select('name').expand('primarycontactid').top(3).execute()") + try: + expanded_records = await backoff( + lambda: client.query.builder("account").select("name").expand("primarycontactid").top(3).execute() + ) + print(f"[OK] Found {len(expanded_records)} accounts with expanded contact:") + for rec in expanded_records: + contact = rec.get("primarycontactid") + contact_name = contact.get("fullname", "(none)") if contact else "(no contact)" + print(f" - '{rec.get('name')}' -> Contact: {contact_name}") + except Exception as e: + print(f"[SKIP] Expand demo skipped (no accounts in org): {e}") + + log_call("ExpandOption('Account_Tasks').select('subject').order_by('createdon', descending=True).top(3)") + try: + tasks_opt = ( + ExpandOption("Account_Tasks").select("subject", "createdon").order_by("createdon", descending=True).top(3) + ) + nested_records = await backoff( + lambda: client.query.builder("account").select("name").expand(tasks_opt).top(3).execute() + ) + print(f"[OK] Found {len(nested_records)} accounts with nested task expansion:") + for rec in nested_records: + tasks = rec.get("Account_Tasks", []) + print(f" - '{rec.get('name')}' has {len(tasks)} task(s)") + except Exception as e: + print(f"[SKIP] Nested expand demo skipped: {e}") + + # ============================================================================ + # 9. SQL QUERY + # ============================================================================ + print("\n" + "=" * 80) + print("9. SQL Query") + print("=" * 80) + + sql = "SELECT new_title, new_quantity FROM new_walkthroughdemo WHERE new_completed = 1" + log_call(f"await client.query.sql('{sql}')") + try: + results = await backoff(lambda: client.query.sql(sql)) + print(f"[OK] SQL query returned {len(results)} completed records:") + for result in results[:5]: + print(f" - new_Title='{result.get('new_title')}', new_Quantity={result.get('new_quantity')}") + except Exception as e: + print(f"[WARN] SQL query failed: {str(e)}") + + # ============================================================================ + # 10. FETCHXML QUERY + # ============================================================================ + print("\n" + "=" * 80) + print("10. FetchXML Query") + print("=" * 80) + + xml = f""" + + + + + + + + + + """ + log_call("await client.query.fetchxml(xml).execute()") + try: + fx_result = await backoff(lambda: client.query.fetchxml(xml).execute()) + print(f"[OK] FetchXML returned {len(fx_result)} incomplete records:") + for r in fx_result[:5]: + print(f" - '{r.get('new_title')}' Quantity={r.get('new_quantity')}") + except Exception as e: + print(f"[WARN] FetchXML query failed: {e}") + + log_call("async for page in client.query.fetchxml(paged_xml).execute_pages()") + paged_xml = f""" + + + + + + + """ + try: + fx_page_num = 0 + fx_total = 0 + async for page in client.query.fetchxml(paged_xml).execute_pages(): + fx_page_num += 1 + fx_total += len(page) + titles = [r.get("new_title", "?") for r in page] + print(f" Page {fx_page_num}: {len(page)} record(s) — {titles}") + print(f"[OK] FetchXML execute_pages(): {fx_total} total records across {fx_page_num} page(s)") + except Exception as e: + print(f"[WARN] FetchXML execute_pages failed: {e}") + + # ============================================================================ + # 11. PICKLIST LABEL CONVERSION + # ============================================================================ + print("\n" + "=" * 80) + print("11. Picklist Label Conversion") + print("=" * 80) + + log_call(f"await client.records.create('{table_name}', {{'new_Priority': 'High'}})") + label_record = { + "new_Title": "Test label conversion", + "new_Quantity": 1, + "new_Amount": 99.99, + "new_Completed": False, + "new_Priority": "High", + } + label_id = await backoff(lambda: client.records.create(table_name, label_record)) + retrieved = await backoff(lambda: client.records.retrieve(table_name, label_id)) + print(f"[OK] Created record with string label 'High' for new_Priority") + print(f" new_Priority stored as integer: {retrieved.get('new_priority')}") + print(f" new_Priority@FormattedValue: {retrieved.get('new_priority@OData.Community.Display.V1.FormattedValue')}") + + log_call(f"await client.records.update('{table_name}', label_id, {{'new_Priority': 'Low'}})") + await backoff(lambda: client.records.update(table_name, label_id, {"new_Priority": "Low"})) + updated_label = await backoff(lambda: client.records.retrieve(table_name, label_id)) + print(f"[OK] Updated record with string label 'Low' for new_Priority") + print(f" new_Priority stored as integer: {updated_label.get('new_priority')}") + + # ============================================================================ + # 12. COLUMN MANAGEMENT + # ============================================================================ + print("\n" + "=" * 80) + print("12. Column Management") + print("=" * 80) + + log_call(f"await client.tables.add_columns('{table_name}', {{'new_Tags': 'string'}})") + created_cols = await backoff(lambda: client.tables.add_columns(table_name, {"new_Tags": "string"})) + print(f"[OK] Added column: {created_cols[0]}") + + log_call(f"await client.tables.remove_columns('{table_name}', ['new_Tags'])") + await backoff(lambda: client.tables.remove_columns(table_name, ["new_Tags"])) + print("[OK] Deleted column: new_Tags") + + # ============================================================================ + # 13. DELETE OPERATIONS + # ============================================================================ + print("\n" + "=" * 80) + print("13. Delete Operations") + print("=" * 80) + + log_call(f"await client.records.delete('{table_name}', '{id1}')") + await backoff(lambda: client.records.delete(table_name, id1)) + print(f"[OK] Deleted single record: {id1}") + + log_call(f"await client.records.delete('{table_name}', [{len(paging_ids)} IDs])") + job_id = await backoff(lambda: client.records.delete(table_name, paging_ids)) + print(f"[OK] Bulk delete job started: {job_id}") + + # ============================================================================ + # 14. BATCH OPERATIONS + # ============================================================================ + print("\n" + "=" * 80) + print("14. Batch Operations") + print("=" * 80) + + log_call("client.batch.new() + batch.records.create(...) x2 + await batch.execute()") + batch = client.batch.new() + batch.records.create( + table_name, + { + "new_Title": "Batch task alpha", + "new_Quantity": 1, + "new_Amount": 25.0, + "new_Completed": False, + "new_Priority": Priority.LOW, + }, + ) + batch.records.create( + table_name, + { + "new_Title": "Batch task beta", + "new_Quantity": 2, + "new_Amount": 50.0, + "new_Completed": False, + "new_Priority": Priority.MEDIUM, + }, + ) + result = await batch.execute() + batch_ids = list(result.entity_ids) + print(f"[OK] Batch create: {len(result.succeeded)} operations, {len(batch_ids)} records created") + + log_call("client.batch.new() + batch.records.retrieve(...) x2 + await batch.execute()") + batch = client.batch.new() + for bid in batch_ids: + batch.records.retrieve(table_name, bid, select=["new_title", "new_quantity"]) + result = await batch.execute() + print(f"[OK] Batch get: {len(result.succeeded)} reads in one HTTP request") + for resp in result.succeeded: + if resp.data: + print(f" new_title='{resp.data.get('new_title')}', new_quantity={resp.data.get('new_quantity')}") + + log_call("async with batch.changeset() as cs: cs.records.create(...); cs.records.update(ref, ...)") + batch = client.batch.new() + async with batch.changeset() as cs: + cs_ref = cs.records.create( + table_name, + { + "new_Title": "Changeset task", + "new_Quantity": 5, + "new_Amount": 100.0, + "new_Completed": False, + "new_Priority": Priority.HIGH, + }, + ) + cs.records.update(table_name, cs_ref, {"new_Completed": True}) + result = await batch.execute() + if not result.has_errors: + batch_ids.extend(result.entity_ids) + print(f"[OK] Changeset: {len(result.succeeded)} operations committed atomically") + else: + for item in result.failed: + print(f"[WARN] Changeset error {item.status_code}: {item.error_message}") + + log_call(f"client.batch.new() + batch.records.delete(...) x{len(batch_ids)} + await batch.execute()") + batch = client.batch.new() + for bid in batch_ids: + batch.records.delete(table_name, bid) + result = await batch.execute(continue_on_error=True) + print(f"[OK] Batch delete: {len(result.succeeded)} records deleted in one HTTP request") + + # ============================================================================ + # 15. CLEANUP + # ============================================================================ + print("\n" + "=" * 80) + print("15. Cleanup") + print("=" * 80) + + log_call(f"await client.tables.delete('{table_name}')") + try: + await backoff(lambda: client.tables.delete(table_name)) + print(f"[OK] Deleted table: {table_name}") + except MetadataError as ex: + if "not found" in str(ex).lower(): + print(f"[OK] Table already removed: {table_name}") + else: + raise + except Exception as ex: + if "404" in str(ex): + print(f"[OK] Table removed: {table_name}") + else: + raise + + # ============================================================================ + # SUMMARY + # ============================================================================ + print("\n" + "=" * 80) + print("Async Walkthrough Complete!") + print("=" * 80) + print("\nDemonstrated operations:") + print(" [OK] Table creation with multiple column types") + print(" [OK] Single and multiple record creation") + print(" [OK] Reading records by ID and with filters") + print(" [OK] Single and multiple record updates") + print(" [OK] Paging through large result sets") + print(" [OK] AsyncQueryBuilder fluent queries (where + col(), col().in_(), col().between(), to_dataframe)") + print(" [OK] Expand navigation properties (simple + nested ExpandOption)") + print(" [OK] SQL queries") + print(" [OK] FetchXML queries (execute + execute_pages)") + print(" [OK] Picklist label-to-value conversion") + print(" [OK] Column management") + print(" [OK] Single and bulk delete operations") + print(" [OK] Batch operations (create, read, changeset, delete)") + print(" [OK] Table cleanup") + print("=" * 80) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/aio/basic/__init__.py b/examples/aio/basic/__init__.py new file mode 100644 index 00000000..9a045456 --- /dev/null +++ b/examples/aio/basic/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. diff --git a/examples/aio/basic/functional_testing.py b/examples/aio/basic/functional_testing.py new file mode 100644 index 00000000..b194050e --- /dev/null +++ b/examples/aio/basic/functional_testing.py @@ -0,0 +1,898 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +""" +PowerPlatform Dataverse Client SDK - Async Functional Testing + +Async equivalent of examples/basic/functional_testing.py. + +This script provides comprehensive async functional testing of the SDK: +- Real environment connection testing +- Table creation and metadata operations +- Full CRUD operations testing +- Query functionality validation (list, list_pages, builder, fetchxml) +- Batch operations (create, read, update, changeset, delete) +- Interactive cleanup options + +Prerequisites: +- PowerPlatform-Dataverse-Client SDK installed (run aio/basic/installation_example.py first) +- Azure Identity credentials configured +- Access to a Dataverse environment with table creation permissions + +Usage: + python examples/aio/basic/functional_testing.py +""" + +import asyncio +import sys +from typing import Optional, Dict, Any +from datetime import datetime + +from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient +from PowerPlatform.Dataverse.core.errors import HttpError, MetadataError +from PowerPlatform.Dataverse.models.relationship import ( + LookupAttributeMetadata, + OneToManyRelationshipMetadata, + ManyToManyRelationshipMetadata, + CascadeConfiguration, +) +from PowerPlatform.Dataverse.models.labels import Label, LocalizedLabel +from PowerPlatform.Dataverse.common.constants import ( + CASCADE_BEHAVIOR_NO_CASCADE, + CASCADE_BEHAVIOR_REMOVE_LINK, +) +from PowerPlatform.Dataverse.models.upsert import UpsertItem +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parents[1])) +from _auth import AsyncInteractiveBrowserCredential + + +def get_dataverse_org_url() -> str: + """Get Dataverse org URL from user input.""" + print("\n-> Dataverse Environment Setup") + print("=" * 50) + + if not sys.stdin.isatty(): + print("[ERR] Interactive input required. Run this script in a terminal.") + sys.exit(1) + + while True: + org_url = input("Enter your Dataverse org URL (e.g., https://yourorg.crm.dynamics.com): ").strip() + if org_url: + return org_url.rstrip("/") + print("[WARN] Please enter a valid URL.") + + +async def backoff(coro_fn, *, delays=(0, 2, 5, 10, 20, 20)): + """Retry a coroutine with exponential back-off for metadata propagation delays.""" + last = None + total_delay = 0 + attempts = 0 + for d in delays: + if d: + await asyncio.sleep(d) + total_delay += d + attempts += 1 + try: + result = await coro_fn() + if attempts > 1: + print(f" * Backoff succeeded after {attempts - 1} retry(s); waited {total_delay}s total.") + return result + except Exception as ex: + last = ex + continue + if last: + if attempts: + print(f" [WARN] Backoff exhausted after {max(attempts - 1, 0)} retry(s); waited {total_delay}s total.") + raise last + + +async def setup_authentication(): + """Set up authentication and create async Dataverse client.""" + print("\n-> Authentication Setup") + print("=" * 50) + + org_url = get_dataverse_org_url() + try: + credential = AsyncInteractiveBrowserCredential() + client = AsyncDataverseClient(org_url, credential) + + print("Testing connection...") + tables = await client.tables.list() + print(f"[OK] Connection successful! Found {len(tables)} tables.") + + user_owned = await client.tables.list( + filter="OwnershipType eq Microsoft.Dynamics.CRM.OwnershipTypes'UserOwned'", + select=["LogicalName", "SchemaName", "DisplayName"], + ) + print(f"[OK] Found {len(user_owned)} user-owned tables (filter + select).") + return client, credential + + except Exception as e: + print(f"[ERR] Authentication failed: {e}") + sys.exit(1) + + +async def wait_for_table_metadata( + client: AsyncDataverseClient, + table_schema_name: str, + retries: int = 10, + delay_seconds: int = 3, +) -> Dict[str, Any]: + """Poll until table metadata is published and entity set becomes available.""" + for attempt in range(1, retries + 1): + try: + info = await client.tables.get(table_schema_name) + if info and info.get("entity_set_name"): + if attempt > 1: + print(f" [OK] Table metadata available after {attempt} attempts.") + return info + except Exception: + pass + + if attempt < retries: + print(f" Waiting for table metadata to publish (attempt {attempt}/{retries})...") + await asyncio.sleep(delay_seconds) + + raise RuntimeError("Table metadata did not become available in time. Please retry later.") + + +async def ensure_test_table(client: AsyncDataverseClient) -> Dict[str, Any]: + """Create or verify test table exists.""" + print("\n-> Test Table Setup") + print("=" * 50) + + table_schema_name = "test_TestSDKFunctionality" + + try: + existing_table = await client.tables.get(table_schema_name) + if existing_table: + print(f"[OK] Test table '{table_schema_name}' already exists") + return existing_table + except Exception: + print(f"Table '{table_schema_name}' not found, creating...") + + try: + print("Creating new test table...") + table_info = await client.tables.create( + table_schema_name, + primary_column="test_name", + columns={ + "test_description": "string", + "test_count": "int", + "test_amount": "decimal", + "test_is_active": "bool", + "test_created_date": "datetime", + }, + ) + print(f"[OK] Created test table: {table_info.get('table_schema_name')}") + print(f" Logical name: {table_info.get('table_logical_name')}") + print(f" Entity set: {table_info.get('entity_set_name')}") + + return await wait_for_table_metadata(client, table_schema_name) + + except MetadataError as e: + print(f"[ERR] Failed to create table: {e}") + sys.exit(1) + + +async def test_create_record(client: AsyncDataverseClient, table_info: Dict[str, Any]) -> str: + """Test record creation.""" + print("\n-> Record Creation Test") + print("=" * 50) + + table_schema_name = table_info.get("table_schema_name") + attr_prefix = table_schema_name.split("_", 1)[0] if "_" in table_schema_name else table_schema_name + retries = 5 + delay_seconds = 3 + + test_data = { + f"{attr_prefix}_name": f"Test Record {datetime.now().strftime('%H:%M:%S')}", + f"{attr_prefix}_description": "This is a test record created by the async SDK functionality test", + f"{attr_prefix}_count": 42, + f"{attr_prefix}_amount": 123.45, + f"{attr_prefix}_is_active": True, + f"{attr_prefix}_created_date": datetime.now().isoformat(), + } + + try: + print("Creating test record...") + created_id: Optional[str] = None + for attempt in range(1, retries + 1): + try: + created_id = await client.records.create(table_schema_name, test_data) + if attempt > 1: + print(f" [OK] Record creation succeeded after {attempt} attempts.") + break + except HttpError as err: + if getattr(err, "status_code", None) == 404 and attempt < retries: + print( + f" Table not ready for create (attempt {attempt}/{retries}). Retrying in {delay_seconds}s..." + ) + await asyncio.sleep(delay_seconds) + continue + raise + + if created_id: + print(f"[OK] Record created successfully!") + print(f" Record ID: {created_id}") + return created_id + else: + raise ValueError("Unexpected response from records.create operation") + + except Exception as e: + print(f"[ERR] Failed to create record: {e}") + sys.exit(1) + + +async def test_read_record( + client: AsyncDataverseClient, + table_info: Dict[str, Any], + record_id: str, +) -> Dict[str, Any]: + """Test record reading.""" + print("\n-> Record Reading Test") + print("=" * 50) + + table_schema_name = table_info.get("table_schema_name") + attr_prefix = table_schema_name.split("_", 1)[0] if "_" in table_schema_name else table_schema_name + retries = 5 + delay_seconds = 3 + + try: + print(f"Reading record: {record_id}") + record = None + for attempt in range(1, retries + 1): + try: + record = await client.records.retrieve(table_schema_name, record_id) + if attempt > 1: + print(f" [OK] Record read succeeded after {attempt} attempts.") + break + except HttpError as err: + if getattr(err, "status_code", None) == 404 and attempt < retries: + print(f" Record not queryable yet (attempt {attempt}/{retries}). Retrying in {delay_seconds}s...") + await asyncio.sleep(delay_seconds) + continue + raise + + if record is None: + raise RuntimeError("Record did not become available in time.") + + print("[OK] Record retrieved successfully!") + for field_name in [ + f"{attr_prefix}_name", + f"{attr_prefix}_description", + f"{attr_prefix}_count", + f"{attr_prefix}_amount", + f"{attr_prefix}_is_active", + ]: + if field_name in record: + print(f" {field_name}: {record[field_name]}") + + # include_annotations + annotation = "OData.Community.Display.V1.FormattedValue" + annotated = await client.records.retrieve( + table_schema_name, + record_id, + select=[f"{attr_prefix}_is_active", f"{attr_prefix}_count"], + include_annotations=annotation, + ) + ann_key = f"{attr_prefix}_is_active@{annotation}" + if annotated is not None and ann_key in annotated: + print(f"[OK] include_annotations verified: {ann_key} = '{annotated[ann_key]}'") + else: + print(f"[WARN] include_annotations: expected key '{ann_key}' not present in response") + + # expand + try: + expanded = await client.records.retrieve( + table_schema_name, + record_id, + select=[f"{attr_prefix}_name"], + expand=["owninguser"], + ) + owner = (expanded.get("owninguser") or {}) if expanded else {} + owner_name = owner.get("fullname") or owner.get("domainname") or "(unknown)" + print(f"[OK] records.retrieve with expand=['owninguser']: owner='{owner_name}'") + except Exception as e: + print(f"[WARN] records.retrieve expand skipped: {e}") + + return record + + except Exception as e: + print(f"[ERR] Failed to read record: {e}") + sys.exit(1) + + +async def test_query_records(client: AsyncDataverseClient, table_info: Dict[str, Any]) -> None: + """Test querying multiple records.""" + print("\n-> Record Query Test") + print("=" * 50) + + table_schema_name = table_info.get("table_schema_name") + attr_prefix = table_schema_name.split("_", 1)[0] if "_" in table_schema_name else table_schema_name + retries = 5 + delay_seconds = 3 + + select_cols = [f"{attr_prefix}_name", f"{attr_prefix}_count", f"{attr_prefix}_amount"] + active_filter = f"{attr_prefix}_is_active eq true" + + try: + # records.list() — eager + print("Querying records with await client.records.list()...") + for attempt in range(1, retries + 1): + try: + result = await client.records.list( + table_schema_name, + select=select_cols, + filter=active_filter, + top=5, + ) + record_count = 0 + for record in result: + record_count += 1 + name = record.get(f"{attr_prefix}_name", "N/A") + count = record.get(f"{attr_prefix}_count", "N/A") + amount = record.get(f"{attr_prefix}_amount", "N/A") + print(f" Record {record_count}: {name} (Count: {count}, Amount: {amount})") + print(f"[OK] records.list() completed! Found {record_count} active records.") + break + except HttpError as err: + if getattr(err, "status_code", None) == 404 and attempt < retries: + print(f" Query retry {attempt}/{retries}. Waiting {delay_seconds}s...") + await asyncio.sleep(delay_seconds) + continue + raise + + # records.list_pages() — lazy + print("\nQuerying records with async for page in client.records.list_pages() (paged)...") + page_num = 0 + total_records = 0 + async for page in client.records.list_pages( + table_schema_name, + select=select_cols, + filter=active_filter, + ): + page_num += 1 + total_records += len(page) + names = [r.get(f"{attr_prefix}_name", "N/A") for r in page] + print(f" Page {page_num}: {len(page)} record(s) — {names}") + print(f"[OK] records.list_pages() completed! {total_records} records across {page_num} page(s).") + + # records.list() with extended params + print("\nQuerying records.list() with orderby / page_size / count / include_annotations...") + annotation = "OData.Community.Display.V1.FormattedValue" + annotated_result = await client.records.list( + table_schema_name, + select=[f"{attr_prefix}_name", f"{attr_prefix}_is_active"], + filter=active_filter, + orderby=[f"{attr_prefix}_name asc"], + page_size=50, + count=True, + include_annotations=annotation, + ) + ann_key = f"{attr_prefix}_is_active@{annotation}" + ann_present = any(ann_key in r for r in annotated_result) + if ann_present: + print(f"[OK] include_annotations verified: '{ann_key}' present in list() results") + else: + print(f"[WARN] include_annotations: '{ann_key}' not found") + print(f"[OK] records.list() with extended params completed! {len(annotated_result)} record(s).") + + # AsyncQueryBuilder + from PowerPlatform.Dataverse.models.filters import col + + print("\nQuerying with AsyncQueryBuilder (.where(col(...)) + .page_size().execute_pages())...") + qb_pages = 0 + qb_total = 0 + async for page in ( + client.query.builder(table_schema_name) + .select(f"{attr_prefix}_name", f"{attr_prefix}_count") + .where(col(f"{attr_prefix}_is_active") == True) + .page_size(10) + .execute_pages() + ): + qb_pages += 1 + qb_total += len(page) + print(f"[OK] AsyncQueryBuilder execute_pages(): {qb_total} records across {qb_pages} page(s).") + + # FetchXML + print("\nQuerying with client.query.fetchxml().execute() ...") + fx_xml = f""" + + + + + + + + + + """ + try: + fx_result = await client.query.fetchxml(fx_xml).execute() + print(f"[OK] FetchXML execute(): {len(fx_result)} active records.") + except Exception as e: + print(f"[WARN] FetchXML query encountered an issue: {e}") + + except Exception as e: + print(f"[WARN] Query test encountered an issue: {e}") + print(" This might be expected if the table is very new.") + + +async def test_batch_all_operations(client: AsyncDataverseClient, table_info: Dict[str, Any]) -> None: + """Test batch operations using the async batch client.""" + print("\n-> Batch Operations Test") + print("=" * 50) + + table_schema_name = table_info.get("table_schema_name") + logical_name = table_info.get("table_logical_name", table_schema_name.lower()) + attr_prefix = table_schema_name.split("_", 1)[0] if "_" in table_schema_name else table_schema_name + all_ids: list = [] + + try: + # [1] CREATE — single + CreateMultiple + print("\n[1/7] Create — single + CreateMultiple") + batch = client.batch.new() + batch.records.create( + table_schema_name, + { + f"{attr_prefix}_name": f"Batch-A {datetime.now().strftime('%H:%M:%S')}", + f"{attr_prefix}_count": 1, + f"{attr_prefix}_is_active": True, + }, + ) + batch.records.create( + table_schema_name, + [ + { + f"{attr_prefix}_name": f"Batch-B {datetime.now().strftime('%H:%M:%S')}", + f"{attr_prefix}_count": 2, + f"{attr_prefix}_is_active": True, + }, + { + f"{attr_prefix}_name": f"Batch-C {datetime.now().strftime('%H:%M:%S')}", + f"{attr_prefix}_count": 3, + f"{attr_prefix}_is_active": True, + }, + ], + ) + result = await batch.execute() + all_ids = list(result.entity_ids) + if result.has_errors: + for item in result.failed: + print(f"[WARN] {item.status_code}: {item.error_message}") + else: + print(f"[OK] {len(result.succeeded)} ops → {len(all_ids)} records created") + + # [2] READ — retrieve + list + query.sql + if all_ids: + annotation = "OData.Community.Display.V1.FormattedValue" + print(f"\n[2/7] Read — records.retrieve + records.list + query.sql") + batch = client.batch.new() + batch.records.retrieve( + table_schema_name, + all_ids[0], + select=[f"{attr_prefix}_name", f"{attr_prefix}_count", f"{attr_prefix}_is_active"], + include_annotations=annotation, + ) + batch.records.list( + table_schema_name, + select=[f"{attr_prefix}_name", f"{attr_prefix}_is_active"], + filter=f"{attr_prefix}_is_active eq true", + orderby=[f"{attr_prefix}_name asc"], + page_size=50, + include_annotations=annotation, + ) + batch.query.sql(f"SELECT TOP 3 {attr_prefix}_name FROM {logical_name}") + result = await batch.execute() + print(f"[OK] {len(result.succeeded)} succeeded, {len(result.failed)} failed") + + # [3] UPDATE — single + multiple + if len(all_ids) >= 2: + print(f"\n[3/7] Update — single PATCH + UpdateMultiple") + batch = client.batch.new() + batch.records.update(table_schema_name, all_ids[0], {f"{attr_prefix}_count": 10}) + batch.records.update(table_schema_name, all_ids[1:], {f"{attr_prefix}_count": 20}) + result = await batch.execute() + print(f"[OK] {len(result.succeeded)} updates succeeded") + + # [4] CHANGESET (happy path) — create + update via content-ID + delete + if all_ids: + print("\n[4/7] Changeset (happy path) — create + update(ref) + delete") + batch = client.batch.new() + async with batch.changeset() as cs: + ref = cs.records.create( + table_schema_name, + { + f"{attr_prefix}_name": f"Batch-D {datetime.now().strftime('%H:%M:%S')}", + f"{attr_prefix}_count": 4, + f"{attr_prefix}_is_active": False, + }, + ) + cs.records.update(table_schema_name, ref, {f"{attr_prefix}_is_active": True}) + cs.records.delete(table_schema_name, all_ids[-1]) + result = await batch.execute() + if result.has_errors: + for item in result.failed: + print(f"[WARN] {item.status_code}: {item.error_message}") + else: + new_id = next(iter(result.entity_ids), None) + if new_id: + all_ids[-1] = new_id + print(f"[OK] {len(result.succeeded)} ops committed atomically") + + # [5] CHANGESET (rollback) + print("\n[5/7] Changeset (rollback) — failing update rolls back create") + batch = client.batch.new() + async with batch.changeset() as cs: + cs.records.create( + table_schema_name, + { + f"{attr_prefix}_name": f"Rollback-test {datetime.now().strftime('%H:%M:%S')}", + f"{attr_prefix}_count": 0, + f"{attr_prefix}_is_active": False, + }, + ) + cs.records.update(table_schema_name, "00000000-0000-0000-0000-000000000001", {f"{attr_prefix}_count": 999}) + result = await batch.execute(continue_on_error=True) + if result.has_errors: + print("[OK] Changeset rollback verified: changeset failed, no records created") + else: + print("[WARN] Expected rollback but changeset succeeded (unexpected)") + all_ids.extend(result.entity_ids) + + # [6] ADD/REMOVE COLUMNS + col_a = f"{attr_prefix}_batch_extra_a" + col_b = f"{attr_prefix}_batch_extra_b" + print(f"\n[6/7] Batch tables.add_columns + tables.remove_columns") + batch = client.batch.new() + batch.tables.add_columns(table_schema_name, {col_a: "string"}) + batch.tables.add_columns(table_schema_name, {col_b: "int"}) + result = await batch.execute() + if not result.has_errors: + print(f"[OK] {len(result.succeeded)} column(s) added: {col_a}, {col_b}") + batch_rm = client.batch.new() + batch_rm.tables.remove_columns(table_schema_name, [col_a, col_b]) + rm_result = await batch_rm.execute(continue_on_error=True) + print(f"[OK] Removed {len(rm_result.succeeded)} batch-added column(s)") + else: + for item in result.failed: + print(f"[WARN] add_columns error {item.status_code}: {item.error_message}") + + # [7] DELETE + if all_ids: + print(f"\n[7/7] Delete — {len(all_ids)} records (use_bulk_delete=False)") + batch = client.batch.new() + batch.records.delete(table_schema_name, all_ids, use_bulk_delete=False) + result = await batch.execute(continue_on_error=True) + print(f"[OK] Deleted {len(result.succeeded)}, failed {len(result.failed)}") + + print("\n[OK] Batch all-operations test completed!") + + except Exception as e: + print(f"[WARN] Batch test encountered an issue: {e}") + if all_ids: + try: + batch = client.batch.new() + batch.records.delete(table_schema_name, all_ids, use_bulk_delete=False) + await batch.execute(continue_on_error=True) + except Exception: + pass + + +async def test_relationships(client: AsyncDataverseClient) -> None: + """Test relationship lifecycle: create tables, 1:N, N:N, query, delete.""" + print("\n-> Relationship Tests") + print("=" * 50) + + rel_parent_schema = "test_RelParent" + rel_child_schema = "test_RelChild" + rel_m2m_schema = "test_RelProject" + + rel_id_1n = None + rel_id_lookup = None + rel_id_nn = None + created_tables = [] + + try: + # Cleanup leftovers + print("Checking for leftover relationship test resources...") + found_leftovers = False + for rel_name in ["test_RelParent_RelChild", "contact_test_relchild_test_ManagerId", "test_relchild_relproject"]: + try: + rel = await client.tables.get_relationship(rel_name) + if rel: + found_leftovers = True + break + except Exception: + pass + + if not found_leftovers: + for tbl in [rel_child_schema, rel_parent_schema, rel_m2m_schema]: + try: + if await client.tables.get(tbl): + found_leftovers = True + break + except Exception: + pass + + if found_leftovers: + cleanup_ok = input("Found leftover test resources. Clean up? (y/N): ").strip().lower() in ["y", "yes"] + if cleanup_ok: + for rel_name in [ + "test_RelParent_RelChild", + "contact_test_relchild_test_ManagerId", + "test_relchild_relproject", + ]: + try: + rel = await client.tables.get_relationship(rel_name) + if rel: + await client.tables.delete_relationship(rel.relationship_id) + print(f" (Cleaned up relationship: {rel_name})") + except Exception: + pass + for tbl in [rel_child_schema, rel_parent_schema, rel_m2m_schema]: + try: + if await client.tables.get(tbl): + await client.tables.delete(tbl) + print(f" (Cleaned up table: {tbl})") + except Exception: + pass + + # Create tables + print("\nCreating relationship test tables...") + + async def _get_or_create(schema, columns, label): + info = await client.tables.get(schema) + if info: + print(f"[OK] Table already exists: {schema} (skipped)") + return info + try: + result = await backoff(lambda: client.tables.create(schema, columns)) + print(f"[OK] Created {label}: {schema}") + return result + except Exception as e: + if "already exists" in str(e).lower() or "not unique" in str(e).lower(): + print(f"[OK] Table already exists: {schema} (skipped)") + return await client.tables.get(schema) + raise + + parent_info = await _get_or_create(rel_parent_schema, {"test_Code": "string"}, "parent table") + created_tables.append(rel_parent_schema) + + child_info = await _get_or_create(rel_child_schema, {"test_Number": "string"}, "child table") + created_tables.append(rel_child_schema) + + proj_info = await _get_or_create(rel_m2m_schema, {"test_ProjectCode": "string"}, "M:N table") + created_tables.append(rel_m2m_schema) + + await wait_for_table_metadata(client, rel_parent_schema) + await wait_for_table_metadata(client, rel_child_schema) + await wait_for_table_metadata(client, rel_m2m_schema) + + # 1:N relationship + print("\n Test 1: Create 1:N relationship") + lookup = LookupAttributeMetadata( + schema_name="test_ParentId", + display_name=Label(localized_labels=[LocalizedLabel(label="Parent", language_code=1033)]), + required_level="None", + ) + relationship = OneToManyRelationshipMetadata( + schema_name="test_RelParent_RelChild", + referenced_entity=parent_info["table_logical_name"], + referencing_entity=child_info["table_logical_name"], + referenced_attribute=f"{parent_info['table_logical_name']}id", + cascade_configuration=CascadeConfiguration( + delete=CASCADE_BEHAVIOR_REMOVE_LINK, + assign=CASCADE_BEHAVIOR_NO_CASCADE, + merge=CASCADE_BEHAVIOR_NO_CASCADE, + ), + ) + existing_1n = await client.tables.get_relationship("test_RelParent_RelChild") + if existing_1n: + rel_id_1n = existing_1n.relationship_id + print(f" [OK] Relationship already exists (skipped)") + else: + result_1n = await backoff( + lambda: client.tables.create_one_to_many_relationship(lookup=lookup, relationship=relationship) + ) + assert result_1n.relationship_schema_name == "test_RelParent_RelChild" + rel_id_1n = result_1n.relationship_id + print(f" [OK] Created 1:N: {result_1n.relationship_schema_name}") + + # Lookup field + print("\n Test 2: Create lookup field (convenience API)") + existing_lookup = await client.tables.get_relationship("contact_test_relchild_test_ManagerId") + if existing_lookup: + rel_id_lookup = existing_lookup.relationship_id + print(f" [OK] Lookup already exists (skipped)") + else: + result_lookup = await backoff( + lambda: client.tables.create_lookup_field( + referencing_table=child_info["table_logical_name"], + lookup_field_name="test_ManagerId", + referenced_table="contact", + display_name="Manager", + description="The record's manager contact", + required=False, + cascade_delete=CASCADE_BEHAVIOR_REMOVE_LINK, + ) + ) + rel_id_lookup = result_lookup.relationship_id + print(f" [OK] Created lookup: {result_lookup.lookup_schema_name}") + + # N:N relationship + print("\n Test 3: Create N:N relationship") + m2m = ManyToManyRelationshipMetadata( + schema_name="test_relchild_relproject", + entity1_logical_name=child_info["table_logical_name"], + entity2_logical_name=proj_info["table_logical_name"], + ) + existing_nn = await client.tables.get_relationship("test_relchild_relproject") + if existing_nn: + rel_id_nn = existing_nn.relationship_id + print(f" [OK] Relationship already exists (skipped)") + else: + result_nn = await backoff(lambda: client.tables.create_many_to_many_relationship(relationship=m2m)) + assert result_nn.relationship_schema_name == "test_relchild_relproject" + rel_id_nn = result_nn.relationship_id + print(f" [OK] Created N:N: {result_nn.relationship_schema_name}") + + # Get relationship metadata + print("\n Test 4: Query relationship metadata") + fetched_1n = await client.tables.get_relationship("test_RelParent_RelChild") + assert fetched_1n is not None and fetched_1n.relationship_type == "one_to_many" + print(f" [OK] Retrieved 1:N: {fetched_1n.relationship_schema_name}") + + fetched_nn = await client.tables.get_relationship("test_relchild_relproject") + assert fetched_nn is not None and fetched_nn.relationship_type == "many_to_many" + print(f" [OK] Retrieved N:N: {fetched_nn.relationship_schema_name}") + + missing = await client.tables.get_relationship("nonexistent_relationship_xyz") + assert missing is None + print(" [OK] Non-existent relationship returns None") + + # Delete relationships + print("\n Test 5: Delete relationships") + await backoff(lambda: client.tables.delete_relationship(rel_id_1n)) + rel_id_1n = None + print(" [OK] Deleted 1:N relationship") + + await backoff(lambda: client.tables.delete_relationship(rel_id_lookup)) + rel_id_lookup = None + print(" [OK] Deleted lookup relationship") + + await backoff(lambda: client.tables.delete_relationship(rel_id_nn)) + rel_id_nn = None + print(" [OK] Deleted N:N relationship") + + verify = await client.tables.get_relationship("test_RelParent_RelChild") + assert verify is None + print(" [OK] Verified 1:N deletion") + + print("\n[OK] All relationship tests passed!") + + finally: + for rid in [rel_id_1n, rel_id_lookup, rel_id_nn]: + if rid: + try: + await client.tables.delete_relationship(rid) + except Exception: + pass + + for tbl in reversed(created_tables): + try: + await backoff(lambda name=tbl: client.tables.delete(name)) + print(f" (Cleaned up table: {tbl})") + except Exception as e: + print(f" [WARN] Could not delete {tbl}: {e}") + + +async def cleanup_test_data( + client: AsyncDataverseClient, + table_info: Dict[str, Any], + record_id: str, +) -> None: + """Clean up test data.""" + print("\n-> Cleanup") + print("=" * 50) + + table_schema_name = table_info.get("table_schema_name") + retries = 5 + delay_seconds = 3 + + cleanup_choice = input("Do you want to delete the test record? (y/N): ").strip().lower() + if cleanup_choice in ["y", "yes"]: + for attempt in range(1, retries + 1): + try: + await client.records.delete(table_schema_name, record_id) + print("[OK] Test record deleted successfully") + break + except HttpError as err: + if getattr(err, "status_code", None) == 404: + print("Record already deleted; skipping.") + break + if attempt < retries: + await asyncio.sleep(delay_seconds) + continue + print(f"[WARN] Failed to delete test record: {err}") + except Exception as e: + print(f"[WARN] Failed to delete test record: {e}") + break + else: + print("Test record kept for inspection") + + table_cleanup = input("Do you want to delete the test table? (y/N): ").strip().lower() + if table_cleanup in ["y", "yes"]: + for attempt in range(1, retries + 1): + try: + await client.tables.delete(table_schema_name) + print("[OK] Test table deleted successfully") + break + except HttpError as err: + if attempt < retries: + await asyncio.sleep(delay_seconds) + continue + print(f"[WARN] Failed to delete test table: {err}") + except Exception as e: + print(f"[WARN] Failed to delete test table: {e}") + break + else: + print("Test table kept for future testing") + + +async def main(): + """Main async test function.""" + print("PowerPlatform Dataverse Client SDK - Async Functional Testing") + print("=" * 70) + print("This script tests async SDK functionality in a real Dataverse environment:") + print(" - Authentication & Connection") + print(" - Table Creation & Metadata Operations") + print(" - Record CRUD Operations") + print(" - Query Functionality (list, list_pages, builder, fetchxml)") + print(" - Relationship Operations (1:N, N:N, lookup)") + print(" - Batch Operations (create, read, update, changeset, delete)") + print(" - Interactive Cleanup") + print("=" * 70) + print("For installation validation, run examples/aio/basic/installation_example.py first") + print("=" * 70) + + try: + client, credential = await setup_authentication() + + try: + async with client: + table_info = await ensure_test_table(client) + record_id = await test_create_record(client, table_info) + await test_read_record(client, table_info, record_id) + await test_query_records(client, table_info) + await test_relationships(client) + await test_batch_all_operations(client, table_info) + + print("\nAsync Functional Test Summary") + print("=" * 50) + print("[OK] Authentication: Success") + print("[OK] Table Operations: Success") + print("[OK] Record Creation: Success") + print("[OK] Record Reading: Success") + print("[OK] Record Querying (list, list_pages, builder, fetchxml): Success") + print("[OK] Relationship Operations: Success") + print("[OK] Batch Operations: Success") + print("\nYour async PowerPlatform Dataverse Client SDK is fully functional!") + + await cleanup_test_data(client, table_info, record_id) + finally: + await credential.close() + + except KeyboardInterrupt: + print("\n\n[WARN] Test interrupted by user") + sys.exit(1) + except Exception as e: + print(f"\n[ERR] Unexpected error: {e}") + sys.exit(1) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/aio/basic/installation_example.py b/examples/aio/basic/installation_example.py new file mode 100644 index 00000000..df958c17 --- /dev/null +++ b/examples/aio/basic/installation_example.py @@ -0,0 +1,372 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +""" +PowerPlatform Dataverse Client - Async Installation, Validation & Usage Example + +Async equivalent of examples/basic/installation_example.py. + +This script demonstrates the async client (AsyncDataverseClient) and validates +that all async imports, classes, and methods are correctly installed. + +## Installation + +```bash +pip install PowerPlatform-Dataverse-Client azure-identity +``` + +## What This Script Does + +- Validates async package imports +- Checks version and package metadata +- Shows async usage patterns +- Offers optional interactive testing with a real Dataverse environment + +Prerequisites for Interactive Testing: +- Access to a Microsoft Dataverse environment +- Azure Identity credentials configured +- Interactive browser access for authentication +""" + +import asyncio +import sys +import subprocess +from datetime import datetime +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parents[1])) + +from PowerPlatform.Dataverse.aio.operations.async_records import AsyncRecordOperations +from PowerPlatform.Dataverse.aio.operations.async_query import AsyncQueryOperations +from PowerPlatform.Dataverse.aio.operations.async_tables import AsyncTableOperations +from PowerPlatform.Dataverse.aio.operations.async_files import AsyncFileOperations + + +def validate_imports(): + """Validate that all key async imports work correctly.""" + print("Validating Async Package Imports...") + print("-" * 50) + + try: + from PowerPlatform.Dataverse import __version__ + from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient + + print(f" [OK] Namespace: PowerPlatform.Dataverse.aio") + print(f" [OK] Package version: {__version__}") + print(f" [OK] Async client: PowerPlatform.Dataverse.aio.async_client.AsyncDataverseClient") + + from PowerPlatform.Dataverse.core.errors import HttpError, MetadataError + + print(f" [OK] Core errors: HttpError, MetadataError") + + from PowerPlatform.Dataverse.core.config import DataverseConfig + + print(f" [OK] Core config: DataverseConfig") + + from PowerPlatform.Dataverse.aio.data._async_odata import _AsyncODataClient + + print(f" [OK] Async data layer: _AsyncODataClient") + + from PowerPlatform.Dataverse.aio.models.async_fetchxml_query import AsyncFetchXmlQuery + from PowerPlatform.Dataverse.aio.models.async_query_builder import AsyncQueryBuilder + + print(f" [OK] Async models: AsyncFetchXmlQuery, AsyncQueryBuilder") + + from _auth import AsyncInteractiveBrowserCredential + + print(f" [OK] Azure Identity: AsyncInteractiveBrowserCredential (interactive browser)") + + return True, __version__, AsyncDataverseClient + + except ImportError as e: + print(f" [ERR] Import failed: {e}") + print("\nTroubleshooting:") + print(" pip install PowerPlatform-Dataverse-Client azure-identity") + print(" Or for development: pip install -e .") + return False, None, None + + +def validate_client_methods(AsyncDataverseClient): + """Validate that AsyncDataverseClient has expected methods.""" + print("\nValidating Async Client Methods...") + print("-" * 50) + + expected_namespaces = { + "records": ["create", "retrieve", "update", "delete", "list", "list_pages", "upsert"], + "query": ["sql", "builder", "fetchxml", "sql_columns", "odata_expands"], + "tables": [ + "create", + "get", + "list", + "delete", + "add_columns", + "remove_columns", + "create_one_to_many_relationship", + "create_many_to_many_relationship", + "delete_relationship", + "get_relationship", + "create_lookup_field", + ], + "files": ["upload"], + } + + ns_classes = { + "records": AsyncRecordOperations, + "query": AsyncQueryOperations, + "tables": AsyncTableOperations, + "files": AsyncFileOperations, + } + + missing_methods = [] + for ns, methods in expected_namespaces.items(): + ns_cls = ns_classes.get(ns) + for method in methods: + attr_path = f"{ns}.{method}" + if ns_cls is not None and hasattr(ns_cls, method): + print(f" [OK] Method exists: {attr_path}") + else: + print(f" [ERR] Method missing: {attr_path}") + missing_methods.append(attr_path) + + return len(missing_methods) == 0 + + +def validate_package_metadata(): + """Validate package metadata from pip.""" + print("\nValidating Package Metadata...") + print("-" * 50) + + try: + result = subprocess.run( + [sys.executable, "-m", "pip", "show", "PowerPlatform-Dataverse-Client"], + capture_output=True, + text=True, + ) + if result.returncode == 0: + for line in result.stdout.split("\n"): + if any(line.startswith(p) for p in ["Name:", "Version:", "Summary:", "Location:"]): + print(f" [OK] {line}") + return True + else: + print(" [ERR] Package not found in pip list") + return False + except Exception as e: + print(f" [ERR] Metadata validation failed: {e}") + return False + + +def show_usage_examples(): + """Display async usage examples.""" + print("\nAsync Usage Examples") + print("=" * 50) + + print(""" +Basic Setup: +```python +import asyncio +import sys +from pathlib import Path +sys.path.insert(0, str(Path(__file__).resolve().parents[1])) +from _auth import AsyncInteractiveBrowserCredential +from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient + +async def main(): + credential = AsyncInteractiveBrowserCredential() + try: + async with AsyncDataverseClient("https://yourorg.crm.dynamics.com", credential) as client: + ... # all operations here + finally: + await credential.close() + +asyncio.run(main()) +``` + +CRUD Operations: +```python +async def main(): + async with AsyncDataverseClient(url, credential) as client: + # Create a record + account_id = await client.records.create("account", {"name": "Contoso Ltd"}) + + # Read a single record by ID + account = await client.records.retrieve("account", account_id) + print(f"Account name: {account['name']}") + + # Update a record + await client.records.update("account", account_id, {"telephone1": "555-0200"}) + + # Delete a record + await client.records.delete("account", account_id) +``` + +Querying Data: +```python +async def main(): + async with AsyncDataverseClient(url, credential) as client: + from PowerPlatform.Dataverse.models.filters import col + + # Fluent query builder + result = await ( + client.query.builder("account") + .select("name", "telephone1") + .where(col("statecode") == 0) + .top(10) + .execute() + ) + for record in result: + print(record["name"]) + + # Lazy paged iteration + async for page in ( + client.query.builder("account") + .select("name") + .page_size(50) + .execute_pages() + ): + for record in page: + print(record["name"]) + + # SQL query + rows = await client.query.sql("SELECT TOP 5 name FROM account") + for row in rows: + print(row["name"]) + + # FetchXML + xml = '' + rows = await client.query.fetchxml(xml).execute() + for row in rows: + print(row["name"]) +``` + +Batch Operations: +```python +async def main(): + async with AsyncDataverseClient(url, credential) as client: + batch = client.batch.new() + batch.records.create("account", {"name": "Alpha"}) + batch.records.create("account", {"name": "Beta"}) + result = await batch.execute() + print(f"Created {len(list(result.entity_ids))} records") + + # Atomic changeset + batch = client.batch.new() + async with batch.changeset() as cs: + ref = cs.records.create("contact", {"firstname": "Alice"}) + cs.records.update("account", account_id, { + "primarycontactid@odata.bind": ref + }) + result = await batch.execute() +``` +""") + + +async def interactive_test(): + """Offer optional interactive testing with real Dataverse environment.""" + print("\nInteractive Testing") + print("=" * 50) + + choice = input("Would you like to test with a real Dataverse environment? (y/N): ").strip().lower() + if choice not in ["y", "yes"]: + print(" Skipping interactive test") + return + + if not sys.stdin.isatty(): + print(" [ERR] Interactive input required for testing") + return + + org_url = input("Enter your Dataverse org URL (e.g., https://yourorg.crm.dynamics.com): ").strip() + if not org_url: + print(" [WARN] No URL provided, skipping test") + return + + try: + from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient + from _auth import AsyncInteractiveBrowserCredential + + print(" Setting up authentication...") + credential = AsyncInteractiveBrowserCredential() + + print(" Creating async client...") + try: + async with AsyncDataverseClient(org_url.rstrip("/"), credential) as client: + print(" Testing connection...") + tables = await client.tables.list() + print(f" [OK] Connection successful!") + print(f" Found {len(tables)} tables in environment") + + custom_tables = await client.tables.list( + filter="IsCustomEntity eq true", + select=["LogicalName", "SchemaName"], + ) + print(f" Found {len(custom_tables)} custom tables (filter + select)") + finally: + await credential.close() + + print("\n Your async SDK is ready for use!") + + except Exception as e: + print(f" [ERR] Interactive test failed: {e}") + print(" This might be due to authentication, network, or permissions") + print(" The SDK imports are still valid for offline development") + + +async def main(): + """Run async installation validation and demonstration.""" + print("PowerPlatform Dataverse Client SDK - Async Installation & Validation") + print("=" * 70) + print(f"Validation Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") + print("=" * 70) + + imports_success, version, AsyncDataverseClient = validate_imports() + if not imports_success: + print("\n[ERR] Import validation failed. Please check installation.") + sys.exit(1) + + methods_success = True + if AsyncDataverseClient: + methods_success = validate_client_methods(AsyncDataverseClient) + if not methods_success: + print("\n[WARN] Some client methods are missing, but basic functionality should work.") + + metadata_success = validate_package_metadata() + + show_usage_examples() + + await interactive_test() + + print("\n" + "=" * 70) + print("VALIDATION SUMMARY") + print("=" * 70) + + results = [ + ("Async Package Imports", imports_success), + ("Async Client Methods", methods_success), + ("Package Metadata", metadata_success), + ] + + all_passed = True + for test_name, success in results: + status = "[OK] PASS" if success else "[ERR] FAIL" + print(f"{test_name:<25} {status}") + if not success: + all_passed = False + + print("=" * 70) + if all_passed: + print("SUCCESS: Async PowerPlatform-Dataverse-Client is properly installed!") + if version: + print(f"Package Version: {version}") + print("\nNext Steps:") + print(" - Run examples/aio/basic/functional_testing.py for a live test") + print(" - Run examples/aio/advanced/walkthrough.py for a full feature tour") + else: + print("[ERR] Some validation checks failed!") + print(" pip uninstall PowerPlatform-Dataverse-Client") + print(" pip install PowerPlatform-Dataverse-Client") + sys.exit(1) + + +if __name__ == "__main__": + print("PowerPlatform-Dataverse-Client SDK - Async Installation Example") + print("=" * 60) + asyncio.run(main()) diff --git a/pyproject.toml b/pyproject.toml index 77a6d492..1ddcc51a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,27 +40,30 @@ dependencies = [ [project.scripts] dataverse-install-claude-skill = "PowerPlatform.Dataverse._skill_installer:main" -dataverse-migrate = "PowerPlatform.Dataverse.migration.migrate_v0_to_v1:main" +dataverse-migrate = "tools.migrate_v0_to_v1:main" [project.optional-dependencies] +async = [ + "aiohttp>=3.9", +] dev = [ "pytest>=7.0.0", "pytest-cov>=4.0.0", + "pytest-asyncio>=0.23.0", "black>=23.0.0", "isort>=5.12.0", "mypy>=1.0.0", "ruff>=0.1.0", - "libcst>=1.0.0", ] migration = ["libcst>=1.0.0"] [tool.setuptools] -package-dir = {"" = "src"} +package-dir = {"" = "src", "tools" = "tools"} zip-safe = false [tool.setuptools.packages.find] -where = ["src"] -include = ["PowerPlatform*"] +where = ["src", "."] +include = ["PowerPlatform*", "tools"] namespaces = false [tool.setuptools.package-data] @@ -96,15 +99,14 @@ select = [ [tool.pytest.ini_options] testpaths = ["tests/unit"] +asyncio_mode = "auto" [tool.coverage.run] source = ["src/PowerPlatform"] -# Migration codemod is an opt-in tool (requires the [migration] extra). -# It is exercised by tests/unit/test_migration_tool.py and tests/unit/test_phase4_ga.py, -# but its CLI plumbing and libcst error-path branches are not meaningfully testable -# under the default coverage scope. Exclude from coverage reporting so the metric -# reflects core SDK code only. -omit = ["src/PowerPlatform/Dataverse/migration/*"] +omit = [ + "*/Dataverse/_skill_installer.py", + "*/Dataverse/extensions/__init__.py", +] [tool.coverage.report] fail_under = 90 diff --git a/src/PowerPlatform/Dataverse/aio/__init__.py b/src/PowerPlatform/Dataverse/aio/__init__.py new file mode 100644 index 00000000..ab39e8d8 --- /dev/null +++ b/src/PowerPlatform/Dataverse/aio/__init__.py @@ -0,0 +1,12 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +""" +Async namespace for the PowerPlatform Dataverse SDK. + +Import the async client via:: + + from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient +""" + +__all__ = [] diff --git a/src/PowerPlatform/Dataverse/aio/async_client.py b/src/PowerPlatform/Dataverse/aio/async_client.py new file mode 100644 index 00000000..30553883 --- /dev/null +++ b/src/PowerPlatform/Dataverse/aio/async_client.py @@ -0,0 +1,252 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +from __future__ import annotations + +from contextlib import asynccontextmanager +from typing import AsyncIterator, Optional + +import aiohttp +from azure.core.credentials_async import AsyncTokenCredential + +from .core._async_auth import _AsyncAuthManager +from ..core.config import DataverseConfig, OperationContext +from .data._async_odata import _AsyncODataClient +from .operations.async_dataframe import AsyncDataFrameOperations +from .operations.async_records import AsyncRecordOperations +from .operations.async_query import AsyncQueryOperations +from .operations.async_files import AsyncFileOperations +from .operations.async_tables import AsyncTableOperations +from .operations.async_batch import AsyncBatchOperations + + +class AsyncDataverseClient: + """ + Async high-level client for Microsoft Dataverse operations. + + This client provides a simple, stable async interface for interacting with + Dataverse environments through the Web API. It handles authentication via + Azure Identity and delegates HTTP operations to an internal + :class:`~PowerPlatform.Dataverse.aio.data._async_odata._AsyncODataClient`. + + Key capabilities: + - OData CRUD operations: create, read, update, delete records + - SQL queries: execute read-only SQL via Web API ``?sql`` parameter + - Table metadata: create, inspect, and delete custom tables; create and delete columns + - File uploads: upload files to file columns with chunking support + + :param base_url: Your Dataverse environment URL, for example + ``"https://org.crm.dynamics.com"``. Trailing slash is automatically removed. + :type base_url: :class:`str` + :param credential: Azure async Identity credential for authentication. + :type credential: ~azure.core.credentials_async.AsyncTokenCredential + :param config: Optional configuration for language, timeouts, and retries. + If not provided, defaults are loaded from :meth:`~PowerPlatform.Dataverse.core.config.DataverseConfig.from_env`. + :type config: ~PowerPlatform.Dataverse.core.config.DataverseConfig or None + :param context: Optional caller-defined context object appended to the + outbound ``User-Agent`` header for plugin/tool attribution. Cannot be used + together with ``config`` -- pass the context via + :class:`~PowerPlatform.Dataverse.core.config.DataverseConfig` instead. + :type context: ~PowerPlatform.Dataverse.core.config.OperationContext or None + + :raises ValueError: If ``base_url`` is missing or empty after trimming. + :raises ValueError: If both ``config`` and ``context`` are provided. + + .. note:: + The client lazily initializes its internal OData client on first use, + allowing lightweight construction without immediate network calls. + + .. note:: + All methods that communicate with the Dataverse Web API may raise + :class:`~PowerPlatform.Dataverse.core.errors.HttpError` on non-successful + HTTP responses (e.g. 401, 403, 404, 429, 500). Individual method + docstrings document only domain-specific exceptions. + + Operations are organized into namespaces: + + - ``client.records`` -- create, update, delete, and get records (single or paginated queries) + - ``client.query`` -- query and search operations + - ``client.tables`` -- table and column metadata management + - ``client.files`` -- file upload operations + - ``client.dataframe`` -- pandas DataFrame wrappers for record CRUD + - ``client.batch`` -- batch multiple operations into a single HTTP request + + The client supports Python's async context manager protocol for automatic + resource cleanup and HTTP connection pooling: + + Example: + **Recommended -- async context manager** (enables HTTP connection pooling):: + + from azure.identity.aio import InteractiveBrowserCredential + from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient + + credential = InteractiveBrowserCredential() + + async with AsyncDataverseClient("https://org.crm.dynamics.com", credential) as client: + record_id = await client.records.create("account", {"name": "Contoso Ltd"}) + await client.records.update("account", record_id, {"telephone1": "555-0100"}) + # Session closed, caches cleared automatically + + **Manual lifecycle**:: + + client = AsyncDataverseClient("https://org.crm.dynamics.com", credential) + try: + record_id = await client.records.create("account", {"name": "Contoso Ltd"}) + finally: + await client.aclose() + """ + + def __init__( + self, + base_url: str, + credential: AsyncTokenCredential, + config: Optional[DataverseConfig] = None, + *, + context: Optional[OperationContext] = None, + ) -> None: + if config is not None and context is not None: + raise ValueError( + "Cannot specify both 'config' and 'context'. " "Pass operation_context via DataverseConfig instead." + ) + self.auth = _AsyncAuthManager(credential) + self._base_url = (base_url or "").rstrip("/") + if not self._base_url: + raise ValueError("base_url is required.") + if config is not None: + self._config = config + elif context is not None: + self._config = DataverseConfig(operation_context=context) + else: + self._config = DataverseConfig.from_env() + self._odata: Optional[_AsyncODataClient] = None + self._session: Optional[aiohttp.ClientSession] = None + self._closed: bool = False + + # Operation namespaces + self.records = AsyncRecordOperations(self) + self.query = AsyncQueryOperations(self) + self.tables = AsyncTableOperations(self) + self.files = AsyncFileOperations(self) + self.dataframe = AsyncDataFrameOperations(self) + self.batch = AsyncBatchOperations(self) + + def _get_odata(self) -> _AsyncODataClient: + """ + Get or create the internal async OData client instance. + + This method implements lazy initialization of the low-level async OData + client, deferring construction until the first API call. When used outside + of an ``async with`` block, a :class:`aiohttp.ClientSession` is created + lazily here so that standalone usage (without a context manager) works + without requiring the caller to manage the session explicitly. + + :return: The lazily-initialized low-level async client. + :rtype: ~PowerPlatform.Dataverse.aio.data._async_odata._AsyncODataClient + """ + if self._odata is None: + if self._session is None: + self._session = aiohttp.ClientSession() + self._odata = _AsyncODataClient( + self.auth, + self._base_url, + self._config, + session=self._session, + ) + return self._odata + + @asynccontextmanager + async def _scoped_odata(self) -> AsyncIterator[_AsyncODataClient]: + """Async context manager yielding the low-level client with a correlation scope.""" + self._check_closed() + od = self._get_odata() + # _call_scope() is a sync context manager (just sets a context var — no I/O). + with od._call_scope(): + yield od + + # ---------------- Context manager / lifecycle ---------------- + + async def __aenter__(self) -> "AsyncDataverseClient": + """Enter the async context manager. + + Creates an :class:`aiohttp.ClientSession` for HTTP connection pooling. + All operations within the ``async with`` block reuse this session for + better performance (TCP and TLS reuse). + + :return: The client instance. + :rtype: AsyncDataverseClient + + :raises RuntimeError: If the client has been closed. + """ + self._check_closed() + if self._session is None: + self._session = aiohttp.ClientSession() + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb) -> None: + """Exit the async context manager with cleanup. + + Calls :meth:`aclose` to release resources. Exceptions are not + suppressed. + """ + await self.aclose() + + async def aclose(self) -> None: + """Close the async client and release resources. + + Closes the HTTP session (if any), clears internal caches, and + marks the client as closed. Safe to call multiple times. After + closing, any operation will raise :class:`RuntimeError`. + + Called automatically when using the client as an async context manager. + + Example:: + + client = AsyncDataverseClient(base_url, credential) + try: + await client.records.create("account", {"name": "Contoso"}) + finally: + await client.aclose() + """ + if self._closed: + return + if self._odata is not None: + await self._odata.close() + self._odata = None + if self._session is not None: + await self._session.close() + self._session = None + self._closed = True + + def _check_closed(self) -> None: + """Raise :class:`RuntimeError` if the client has been closed.""" + if self._closed: + raise RuntimeError("AsyncDataverseClient is closed") + + # ---------------- Cache utilities ---------------- + + async def flush_cache(self, kind: str) -> int: + """ + Flush cached client metadata or state. + + :param kind: Cache kind to flush. Currently supported values: + + - ``"picklist"``: Clears picklist label cache used for label-to-integer conversion + + Future kinds (e.g. ``"entityset"``, ``"primaryid"``) may be added without + breaking this signature. + :type kind: :class:`str` + + :return: Number of cache entries removed. + :rtype: :class:`int` + + Example: + Clear the picklist cache:: + + removed = await client.flush_cache("picklist") + print(f"Cleared {removed} cached picklist entries") + """ + async with self._scoped_odata() as od: + return od._flush_cache(kind) + + +__all__ = ["AsyncDataverseClient"] diff --git a/src/PowerPlatform/Dataverse/aio/core/__init__.py b/src/PowerPlatform/Dataverse/aio/core/__init__.py new file mode 100644 index 00000000..2f0cb105 --- /dev/null +++ b/src/PowerPlatform/Dataverse/aio/core/__init__.py @@ -0,0 +1,9 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +""" +Async core infrastructure components for the Dataverse SDK. + +This module contains the foundational async components including authentication, +HTTP client, and error handling. +""" diff --git a/src/PowerPlatform/Dataverse/aio/core/_async_auth.py b/src/PowerPlatform/Dataverse/aio/core/_async_auth.py new file mode 100644 index 00000000..ca25d5b1 --- /dev/null +++ b/src/PowerPlatform/Dataverse/aio/core/_async_auth.py @@ -0,0 +1,45 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +""" +Async authentication helpers for Dataverse. + +This module provides :class:`~PowerPlatform.Dataverse.aio.core._async_auth._AsyncAuthManager`, +a thin wrapper over any Azure Identity ``AsyncTokenCredential`` for acquiring OAuth2 access +tokens asynchronously, and reuses :class:`~PowerPlatform.Dataverse.core._auth._TokenPair` for +storing the acquired token alongside its scope. +""" + +from __future__ import annotations + +from azure.core.credentials_async import AsyncTokenCredential + +from ...core._auth import _TokenPair + + +class _AsyncAuthManager: + """ + Azure Identity-based async authentication manager for Dataverse. + + :param credential: Azure Identity async credential implementation. + :type credential: ~azure.core.credentials_async.AsyncTokenCredential + :raises TypeError: If ``credential`` does not implement :class:`~azure.core.credentials_async.AsyncTokenCredential`. + """ + + def __init__(self, credential: AsyncTokenCredential) -> None: + if not isinstance(credential, AsyncTokenCredential): + raise TypeError("credential must implement azure.core.credentials_async.AsyncTokenCredential.") + self.credential: AsyncTokenCredential = credential + + async def _acquire_token(self, scope: str) -> _TokenPair: + """ + Acquire an access token asynchronously for the specified OAuth2 scope. + + :param scope: OAuth2 scope string, typically ``"https://.crm.dynamics.com/.default"``. + :type scope: :class:`str` + :return: Token pair containing the scope and access token. + :rtype: ~PowerPlatform.Dataverse.core._auth._TokenPair + :raises ~azure.core.exceptions.ClientAuthenticationError: If token acquisition fails. + """ + token = await self.credential.get_token(scope) + return _TokenPair(resource=scope, access_token=token.token) diff --git a/src/PowerPlatform/Dataverse/aio/core/_async_http.py b/src/PowerPlatform/Dataverse/aio/core/_async_http.py new file mode 100644 index 00000000..402be8d4 --- /dev/null +++ b/src/PowerPlatform/Dataverse/aio/core/_async_http.py @@ -0,0 +1,185 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +""" +Async HTTP client with automatic retry logic and timeout handling. + +This module provides :class:`~PowerPlatform.Dataverse.aio.core._async_http._AsyncHttpClient`, +a wrapper around the aiohttp library that adds configurable retry behavior for transient +network errors and intelligent timeout management based on HTTP method types. +""" + +from __future__ import annotations + +import asyncio +import json as _json +import time +from typing import TYPE_CHECKING, Any, Dict, Optional + +import aiohttp + +if TYPE_CHECKING: + from ...core._http_logger import _HttpLogger + + +class _AsyncResponse: + """Materialized HTTP response returned by :class:`_AsyncHttpClient._request`. + + The body is fully buffered before this object is constructed, so all + accessors are synchronous — no ``await`` required. + + :param status: HTTP status code. + :param headers: Response headers as a plain dict. + :param body: Raw response body bytes. + """ + + __slots__ = ("status", "status_code", "headers", "_body") + + def __init__(self, status: int, headers: Dict[str, str], body: bytes) -> None: + self.status = status + self.status_code = status + self.headers = headers + self._body = body + + @property + def text(self) -> str: + """Response body decoded as UTF-8 text.""" + return self._body.decode("utf-8", errors="replace") if self._body else "" + + def json(self, content_type: Any = None) -> Any: + """Parse and return the response body as JSON.""" + return _json.loads(self._body) if self._body else {} + + +class _AsyncHttpClient: + """ + Async HTTP client with configurable retry logic and timeout handling. + + Provides automatic retry behavior for transient failures and default timeout + management for different HTTP methods. + + :param retries: Maximum number of retry attempts for transient errors. Default is 5. + :type retries: :class:`int` | None + :param backoff: Base delay in seconds between retry attempts. Default is 0.5. + :type backoff: :class:`float` | None + :param timeout: Default request timeout in seconds. If None, uses per-method defaults. + :type timeout: :class:`float` | None + :param session: ``aiohttp.ClientSession`` for HTTP connection pooling. + The session is owned by the caller (``AsyncDataverseClient``) and must remain + open for the lifetime of this client. Unlike the sync client, there is no + per-request fallback — a session must always be provided before making requests. + :type session: :class:`aiohttp.ClientSession` | None + :param logger: Optional HTTP diagnostics logger. When provided, all requests, + responses, and transport errors are logged with automatic header redaction. + :type logger: ~PowerPlatform.Dataverse.core._http_logger._HttpLogger | None + """ + + def __init__( + self, + retries: Optional[int] = None, + backoff: Optional[float] = None, + timeout: Optional[float] = None, + session: Optional[aiohttp.ClientSession] = None, + logger: Optional["_HttpLogger"] = None, + ) -> None: + self.max_attempts = retries if retries is not None else 5 + self.base_delay = backoff if backoff is not None else 0.5 + self.default_timeout: Optional[float] = timeout + self._session = session + self._logger = logger + + async def _request(self, method: str, url: str, **kwargs: Any) -> _AsyncResponse: + """ + Execute an HTTP request asynchronously with automatic retry logic and timeout management. + + Applies default timeouts based on HTTP method (120s for POST/DELETE, 10s for others) + and retries on network errors with exponential backoff. + + The response body is fully buffered and returned as a :class:`_AsyncResponse` whose + accessors (``.text``, ``.json()``) are synchronous — no ``await`` required on the caller side. + + :param method: HTTP method (GET, POST, PUT, DELETE, etc.). + :type method: :class:`str` + :param url: Target URL for the request. + :type url: :class:`str` + :param kwargs: Additional arguments passed to ``aiohttp.ClientSession.request()``, + including headers, data, etc. + :return: Materialized HTTP response with body fully buffered. + :rtype: :class:`_AsyncResponse` + :raises aiohttp.ClientError: If all retry attempts fail. + :raises RuntimeError: If no session has been set. + """ + if self._session is None: + raise RuntimeError("No aiohttp.ClientSession set. Set _session before making requests.") + + # If no timeout is provided, use the user-specified default timeout if set; + # otherwise, apply per-method defaults (120s for POST/DELETE, 10s for others). + if "timeout" not in kwargs: + if self.default_timeout is not None: + t = self.default_timeout + else: + m = (method or "").lower() + t = 120 if m in ("post", "delete") else 10 + kwargs["timeout"] = aiohttp.ClientTimeout(total=t) + + # Log outbound request once (before retry loop). + # Use explicit key presence checks so falsy values (e.g. {}) are logged correctly. + if self._logger is not None: + if "json" in kwargs: + req_body = kwargs["json"] + elif "data" in kwargs: + req_body = kwargs["data"] + else: + req_body = None + self._logger.log_request( + method, + url, + headers=kwargs.get("headers"), + body=req_body, + ) + + # Small backoff retry on network errors only + for attempt in range(self.max_attempts): + try: + t0 = time.monotonic() + async with self._session.request(method, url, **kwargs) as resp: + body = await resp.read() + materialized = _AsyncResponse(resp.status, dict(resp.headers), body) + elapsed_ms = (time.monotonic() - t0) * 1000 + + if self._logger is not None: + # Only decode text when body logging is enabled — avoids + # unnecessary overhead for large payloads when max_body_bytes == 0. + resp_body = materialized.text if self._logger.body_logging_enabled else None + self._logger.log_response( + method, + url, + status_code=materialized.status, + headers=materialized.headers, + body=resp_body, + elapsed_ms=elapsed_ms, + ) + return materialized + except (aiohttp.ClientError, asyncio.TimeoutError) as exc: + if self._logger is not None: + self._logger.log_error( + method, + url, + exc, + attempt=attempt + 1, + max_attempts=self.max_attempts, + ) + if attempt == self.max_attempts - 1: + raise + delay = self.base_delay * (2**attempt) + await asyncio.sleep(delay) + continue + + async def close(self) -> None: + """Close the HTTP client and release resources. + + If a session was provided, closes it. Safe to call multiple times. + """ + if self._session is not None: + await self._session.close() + self._session = None diff --git a/src/PowerPlatform/Dataverse/aio/data/__init__.py b/src/PowerPlatform/Dataverse/aio/data/__init__.py new file mode 100644 index 00000000..be8f223c --- /dev/null +++ b/src/PowerPlatform/Dataverse/aio/data/__init__.py @@ -0,0 +1,9 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +""" +Async data access layer for the Dataverse SDK. + +This module contains async OData protocol handling, CRUD operations, metadata management, +SQL query functionality, and file upload capabilities. +""" diff --git a/src/PowerPlatform/Dataverse/aio/data/_async_batch.py b/src/PowerPlatform/Dataverse/aio/data/_async_batch.py new file mode 100644 index 00000000..967929dd --- /dev/null +++ b/src/PowerPlatform/Dataverse/aio/data/_async_batch.py @@ -0,0 +1,295 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +"""Async batch intent resolver and dispatcher for the Dataverse Web API.""" + +from __future__ import annotations + +import uuid +from typing import TYPE_CHECKING, Any, List, Union + +from ...core.errors import MetadataError, ValidationError +from ...core._error_codes import METADATA_TABLE_NOT_FOUND, METADATA_COLUMN_NOT_FOUND +from ...models.batch import BatchResult +from ...data._raw_request import _RawRequest +from ...data._batch_base import ( + _BatchBase, + _RecordCreate, + _RecordUpdate, + _RecordDelete, + _RecordGet, + _RecordList, + _RecordUpsert, + _TableCreate, + _TableDelete, + _TableGet, + _TableList, + _TableAddColumns, + _TableRemoveColumns, + _TableCreateOneToMany, + _TableCreateManyToMany, + _TableDeleteRelationship, + _TableGetRelationship, + _TableCreateLookupField, + _QuerySql, + _ChangeSet, + _ChangeSetBatchItem, + _MAX_BATCH_SIZE, +) + +if TYPE_CHECKING: + from ._async_odata import _AsyncODataClient + +__all__ = [] + + +# --------------------------------------------------------------------------- +# Batch client: resolves intents → raw requests → multipart body → HTTP → result +# --------------------------------------------------------------------------- + + +class _AsyncBatchClient(_BatchBase): + """ + Async version of the Dataverse batch client. + + Serialises a list of intent objects into an OData ``$batch`` multipart/mixed + request, dispatches it asynchronously, and parses the response. + + :param od: The active async OData client (provides helpers and HTTP transport). + """ + + # ------------------------------------------------------------------ + # Public entry point + # ------------------------------------------------------------------ + + async def execute( + self, + items: List[Any], + continue_on_error: bool = False, + ) -> BatchResult: + """ + Resolve all intent objects, build the batch body, send it, and return results. + + Metadata pre-resolution (GET calls for MetadataId) happens here, asynchronously, + before the multipart body is assembled. + """ + if not items: + return BatchResult() + + resolved = await self._resolve_all(items) + + total = sum(len(r.requests) if isinstance(r, _ChangeSetBatchItem) else 1 for r in resolved) + if total > _MAX_BATCH_SIZE: + raise ValidationError( + f"Batch contains {total} operations, which exceeds the limit of " + f"{_MAX_BATCH_SIZE}. Split into multiple batches.", + subcode="batch_size_exceeded", + details={"count": total, "max": _MAX_BATCH_SIZE}, + ) + + batch_boundary = f"batch_{uuid.uuid4()}" + body = self._build_batch_body(resolved, batch_boundary) + + headers: dict[str, str] = { + "Content-Type": f'multipart/mixed; boundary="{batch_boundary}"', + } + if continue_on_error: + headers["Prefer"] = "odata.continue-on-error" + + url = f"{self._od.api}/$batch" + r = await self._od._request( + "post", + url, + data=body.encode("utf-8"), + headers=headers, + # 400 is expected: Dataverse returns 400 for top-level batch + # errors (e.g. malformed body). We parse the response body to + # surface the service error via _parse_batch_response / + # _raise_top_level_batch_error rather than letting _request raise. + expected=(200, 202, 207, 400), + ) + + return self._parse_batch_response(r) + + # ------------------------------------------------------------------ + # Intent resolution dispatcher + # ------------------------------------------------------------------ + + async def _resolve_all(self, items: List[Any]) -> List[Union[_RawRequest, _ChangeSetBatchItem]]: + result: List[Union[_RawRequest, _ChangeSetBatchItem]] = [] + for item in items: + if isinstance(item, _ChangeSet): + if not item.operations: + # Empty changeset — nothing to send; skip silently. + continue + cs_requests: List[_RawRequest] = [] + for op in item.operations: + cs_requests.append(await self._resolve_one(op)) + result.append(_ChangeSetBatchItem(requests=cs_requests)) + else: + result.extend(await self._resolve_item(item)) + return result + + async def _resolve_item(self, item: Any) -> List[_RawRequest]: + """Resolve a single intent to one or more _RawRequest objects.""" + if isinstance(item, _RecordCreate): + return await self._resolve_record_create(item) + if isinstance(item, _RecordUpdate): + return await self._resolve_record_update(item) + if isinstance(item, _RecordDelete): + return await self._resolve_record_delete(item) + if isinstance(item, _RecordGet): + return await self._resolve_record_get(item) + if isinstance(item, _RecordList): + return await self._resolve_record_list(item) + if isinstance(item, _RecordUpsert): + return await self._resolve_record_upsert(item) + if isinstance(item, _TableCreate): + return self._resolve_table_create(item) # sync; inherited from _BatchBase + if isinstance(item, _TableDelete): + return await self._resolve_table_delete(item) + if isinstance(item, _TableGet): + return self._resolve_table_get(item) # sync; inherited from _BatchBase + if isinstance(item, _TableList): + return self._resolve_table_list(item) # sync; inherited from _BatchBase + if isinstance(item, _TableAddColumns): + return await self._resolve_table_add_columns(item) + if isinstance(item, _TableRemoveColumns): + return await self._resolve_table_remove_columns(item) + if isinstance(item, _TableCreateOneToMany): + return self._resolve_table_create_one_to_many(item) # sync; inherited from _BatchBase + if isinstance(item, _TableCreateManyToMany): + return self._resolve_table_create_many_to_many(item) # sync; inherited from _BatchBase + if isinstance(item, _TableDeleteRelationship): + return self._resolve_table_delete_relationship(item) # sync; inherited from _BatchBase + if isinstance(item, _TableGetRelationship): + return self._resolve_table_get_relationship(item) # sync; inherited from _BatchBase + if isinstance(item, _TableCreateLookupField): + return self._resolve_table_create_lookup_field(item) # sync; inherited from _BatchBase + if isinstance(item, _QuerySql): + return await self._resolve_query_sql(item) + raise ValidationError( + f"Unknown batch item type: {type(item).__name__}", + subcode="unknown_batch_item", + ) + + async def _resolve_one(self, item: Any) -> _RawRequest: + """Resolve a changeset operation to exactly one _RawRequest.""" + resolved = await self._resolve_item(item) + if len(resolved) != 1: + raise ValidationError( + "Changeset operations must each produce exactly one HTTP request.", + subcode="changeset_multi_request", + ) + return resolved[0] + + # ------------------------------------------------------------------ + # Record resolvers — delegate to _AsyncODataClient._build_* methods + # ------------------------------------------------------------------ + + async def _resolve_record_create(self, op: _RecordCreate) -> List[_RawRequest]: + entity_set = await self._od._entity_set_from_schema_name(op.table) + if isinstance(op.data, dict): + return [await self._od._build_create(entity_set, op.table, op.data, content_id=op.content_id)] + return [await self._od._build_create_multiple(entity_set, op.table, op.data)] + + async def _resolve_record_update(self, op: _RecordUpdate) -> List[_RawRequest]: + if isinstance(op.ids, str): + if not isinstance(op.changes, dict): + raise TypeError("For single id, changes must be a dict") + return [await self._od._build_update(op.table, op.ids, op.changes, content_id=op.content_id)] + entity_set = await self._od._entity_set_from_schema_name(op.table) + return [await self._od._build_update_multiple(entity_set, op.table, op.ids, op.changes)] + + async def _resolve_record_delete(self, op: _RecordDelete) -> List[_RawRequest]: + if isinstance(op.ids, str): + return [await self._od._build_delete(op.table, op.ids, content_id=op.content_id)] + ids = [rid for rid in op.ids if rid] + if not ids: + return [] + if op.use_bulk_delete: + return [await self._od._build_delete_multiple(op.table, ids)] + return [await self._od._build_delete(op.table, rid) for rid in ids] + + async def _resolve_record_get(self, op: _RecordGet) -> List[_RawRequest]: + return [ + await self._od._build_get( + op.table, + op.record_id, + select=op.select, + expand=op.expand, + include_annotations=op.include_annotations, + ) + ] + + async def _resolve_record_list(self, op: _RecordList) -> List[_RawRequest]: + return [ + await self._od._build_list( + op.table, + select=op.select, + filter=op.filter, + orderby=op.orderby, + top=op.top, + expand=op.expand, + page_size=op.page_size, + count=op.count, + include_annotations=op.include_annotations, + ) + ] + + async def _resolve_record_upsert(self, op: _RecordUpsert) -> List[_RawRequest]: + entity_set = await self._od._entity_set_from_schema_name(op.table) + if len(op.items) == 1: + item = op.items[0] + return [await self._od._build_upsert(entity_set, op.table, item.alternate_key, item.record)] + alternate_keys = [i.alternate_key for i in op.items] + records = [i.record for i in op.items] + return [await self._od._build_upsert_multiple(entity_set, op.table, alternate_keys, records)] + + # ------------------------------------------------------------------ + # Table resolvers — delegate to _AsyncODataClient._build_* methods + # (pre-resolution GETs for MetadataId remain here; they are batch- + # specific lookups needed before the relevant _build_* call) + # ------------------------------------------------------------------ + + async def _require_entity_metadata(self, table: str) -> str: + """Look up MetadataId for *table*, raising MetadataError if not found.""" + ent = await self._od._get_entity_by_table_schema_name(table) + if not ent or not ent.get("MetadataId"): + raise MetadataError( + f"Table '{table}' not found.", + subcode=METADATA_TABLE_NOT_FOUND, + ) + return ent["MetadataId"] + + async def _resolve_table_delete(self, op: _TableDelete) -> List[_RawRequest]: + metadata_id = await self._require_entity_metadata(op.table) + return [self._od._build_delete_entity(metadata_id)] + + async def _resolve_table_add_columns(self, op: _TableAddColumns) -> List[_RawRequest]: + metadata_id = await self._require_entity_metadata(op.table) + return [self._od._build_create_column(metadata_id, col_name, dtype) for col_name, dtype in op.columns.items()] + + async def _resolve_table_remove_columns(self, op: _TableRemoveColumns) -> List[_RawRequest]: + columns = [op.columns] if isinstance(op.columns, str) else list(op.columns) + metadata_id = await self._require_entity_metadata(op.table) + attr_metas = [ + await self._od._get_attribute_metadata(metadata_id, col_name, extra_select="@odata.type,AttributeType") + for col_name in columns + ] + requests: List[_RawRequest] = [] + for col_name, attr_meta in zip(columns, attr_metas): + if not attr_meta or not attr_meta.get("MetadataId"): + raise MetadataError( + f"Column '{col_name}' not found on table '{op.table}'.", + subcode=METADATA_COLUMN_NOT_FOUND, + ) + requests.append(self._od._build_delete_column(metadata_id, attr_meta["MetadataId"])) + return requests + + # ------------------------------------------------------------------ + # Query resolvers — delegate to _AsyncODataClient._build_* methods + # ------------------------------------------------------------------ + + async def _resolve_query_sql(self, op: _QuerySql) -> List[_RawRequest]: + return [await self._od._build_sql(op.sql)] diff --git a/src/PowerPlatform/Dataverse/aio/data/_async_odata.py b/src/PowerPlatform/Dataverse/aio/data/_async_odata.py new file mode 100644 index 00000000..08bd9327 --- /dev/null +++ b/src/PowerPlatform/Dataverse/aio/data/_async_odata.py @@ -0,0 +1,1835 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +"""Async Dataverse Web API client with CRUD, SQL query, and table/column metadata management.""" + +from __future__ import annotations + +__all__ = [] + +import asyncio +import json +import time +import warnings +from datetime import datetime, timezone +from typing import TYPE_CHECKING, Any, AsyncIterator, Dict, List, Optional, Union + +if TYPE_CHECKING: + import aiohttp + +from urllib.parse import quote as _url_quote + +from ..core._async_http import _AsyncHttpClient, _AsyncResponse +from ._async_upload import _AsyncFileUploadMixin +from ._async_relationships import _AsyncRelationshipOperationsMixin +from ...core.errors import * +from ...data._raw_request import _RawRequest +from ...core._error_codes import ( + _http_subcode, + _is_transient_status, + VALIDATION_SQL_NOT_STRING, + VALIDATION_SQL_EMPTY, + VALIDATION_UNSUPPORTED_COLUMN_TYPE, + METADATA_ENTITYSET_NOT_FOUND, + METADATA_ENTITYSET_NAME_MISSING, + METADATA_TABLE_NOT_FOUND, + METADATA_TABLE_ALREADY_EXISTS, + METADATA_COLUMN_NOT_FOUND, +) + +from ...data._odata_base import ( + _ODataBase, + _GUID_RE, + _extract_pagingcookie, + _USER_AGENT, + _DEFAULT_EXPECTED_STATUSES, + _RequestContext, +) + + +class _AsyncODataClient(_AsyncFileUploadMixin, _AsyncRelationshipOperationsMixin, _ODataBase): + """Async Dataverse Web API client: CRUD, SQL-over-API, and table metadata helpers.""" + + def __init__( + self, + auth, + base_url: str, + config=None, + session: Optional[aiohttp.ClientSession] = None, + ) -> None: + """Initialize the async OData client. + + Sets up authentication, base URL, configuration, and internal caches. + + :param auth: Async authentication manager providing ``_acquire_token(scope)`` that returns an object with ``access_token``. + :type auth: ~PowerPlatform.Dataverse.aio.core._async_auth._AsyncAuthManager + :param base_url: Organization base URL (e.g. ``"https://.crm.dynamics.com"``). + :type base_url: ``str`` + :param config: Optional Dataverse configuration (HTTP retry, backoff, timeout, language code). If omitted ``DataverseConfig.from_env()`` is used. + :type config: ~PowerPlatform.Dataverse.core.config.DataverseConfig | ``None`` + :param session: ``aiohttp.ClientSession`` for HTTP connection pooling. Must remain open for the lifetime of this client. + :type session: :class:`aiohttp.ClientSession` | ``None`` + :raises ValueError: If ``base_url`` is empty after stripping. + """ + super().__init__(base_url, config) + self.auth = auth + self._http = _AsyncHttpClient( + retries=self.config.http_retries, + backoff=self.config.http_backoff, + timeout=self.config.http_timeout, + session=session, + logger=self._http_logger, + ) + # Prevents concurrent coroutines from racing through the picklist TTL check + # and issuing redundant metadata fetches. + self._picklist_cache_lock: asyncio.Lock = asyncio.Lock() + + async def close(self) -> None: + """Close the async OData client and release resources. + + Clears all internal caches and closes the underlying HTTP client. + Safe to call multiple times. + """ + super().close() # sync: clears caches, closes logger + if self._http is not None: + await self._http.close() + + async def _headers(self) -> Dict[str, str]: + """Build standard OData headers with bearer auth.""" + scope = f"{self.base_url}/.default" + token = (await self.auth._acquire_token(scope)).access_token + ua = f"{_USER_AGENT} ({self._operation_context})" if self._operation_context else _USER_AGENT + return { + "Authorization": f"Bearer {token}", + "Accept": "application/json", + "Content-Type": "application/json", + "OData-MaxVersion": "4.0", + "OData-Version": "4.0", + "User-Agent": ua, + } + + async def _raw_request(self, method: str, url: str, **kwargs) -> _AsyncResponse: + return await self._http._request(method, url, **kwargs) + + async def _request( + self, + method: str, + url: str, + *, + expected: tuple[int, ...] = _DEFAULT_EXPECTED_STATUSES, + **kwargs, + ) -> _AsyncResponse: + # Acquire base headers once (async), then use a sync closure for _RequestContext.build. + # _RequestContext.build is a sync classmethod defined in _ODataBase and shared by both + # sync and async clients — keeping it sync avoids duplicating the header-injection logic. + base_headers = await self._headers() + + def _merge(h: Optional[Dict[str, str]]) -> Dict[str, str]: + if not h: + return base_headers.copy() + merged = base_headers.copy() + merged.update(h) + return merged + + request_context = _RequestContext.build( + method, + url, + expected=expected, + merge_headers=_merge, + **kwargs, + ) + + r = await self._raw_request(request_context.method, request_context.url, **request_context.kwargs) + if r.status in request_context.expected: + return r + + response_headers = getattr(r, "headers", {}) or {} + raw_text = r.text + body_excerpt = raw_text[:200] + svc_code = None + msg = f"HTTP {r.status}" + try: + data = json.loads(raw_text) if raw_text else {} + if isinstance(data, dict): + inner = data.get("error") + if isinstance(inner, dict): + svc_code = inner.get("code") + imsg = inner.get("message") + if isinstance(imsg, str) and imsg.strip(): + msg = imsg.strip() + else: + imsg2 = data.get("message") + if isinstance(imsg2, str) and imsg2.strip(): + msg = imsg2.strip() + except Exception: + pass + sc = r.status + subcode = _http_subcode(sc) + request_id = ( + response_headers.get("x-ms-service-request-id") + or response_headers.get("req_id") + or response_headers.get("x-ms-request-id") + ) + traceparent = response_headers.get("traceparent") + ra = response_headers.get("Retry-After") + retry_after = None + if ra: + try: + retry_after = int(ra) + except Exception: + retry_after = None + is_transient = _is_transient_status(sc) + raise HttpError( + msg, + status_code=sc, + subcode=subcode, + service_error_code=svc_code, + correlation_id=request_context.headers.get( + "x-ms-correlation-id" + ), # this is a value set on client side, although it's logged on server side too + client_request_id=request_context.headers.get( + "x-ms-client-request-id" + ), # this is a value set on client side, although it's logged on server side too + service_request_id=request_id, + traceparent=traceparent, + body_excerpt=body_excerpt, + retry_after=retry_after, + is_transient=is_transient, + ) + + async def _execute_raw( + self, + req: _RawRequest, + *, + expected: tuple[int, ...] = _DEFAULT_EXPECTED_STATUSES, + ) -> _AsyncResponse: + """Execute a ``_RawRequest`` and return the HTTP response. + + Encodes the pre-serialised body (if present) as UTF-8 and merges any + per-request headers into the standard OData header set. + """ + kwargs: Dict[str, Any] = {} + if req.body is not None: + kwargs["data"] = req.body.encode("utf-8") + if req.headers: + kwargs["headers"] = req.headers + return await self._request(req.method.lower(), req.url, expected=expected, **kwargs) + + # --- CRUD Internal functions --- + async def _create(self, entity_set: str, table_schema_name: str, record: Dict[str, Any]) -> str: + """Create a single record and return its GUID. + + :param entity_set: Resolved entity set (plural) name. + :type entity_set: ``str`` + :param table_schema_name: Schema name of the table. + :type table_schema_name: ``str`` + :param record: Attribute payload mapped by logical column names. + :type record: ``dict[str, Any]`` + + :return: Created record GUID. + :rtype: ``str`` + + .. note:: + Relies on ``OData-EntityId`` (canonical) or ``Location`` response header. No response body parsing is performed. Raises ``RuntimeError`` if neither header contains a GUID. + """ + r = await self._execute_raw(await self._build_create(entity_set, table_schema_name, record)) + ent_loc = r.headers.get("OData-EntityId") or r.headers.get("OData-EntityID") + if ent_loc: + m = _GUID_RE.search(ent_loc) + if m: + return m.group(0) + loc = r.headers.get("Location") + if loc: + m = _GUID_RE.search(loc) + if m: + return m.group(0) + header_keys = ", ".join(sorted(r.headers.keys())) + raise RuntimeError( + f"Create response missing GUID in OData-EntityId/Location headers (status={r.status}). Headers: {header_keys}" + ) + + async def _create_multiple( + self, + entity_set: str, + table_schema_name: str, + records: List[Dict[str, Any]], + ) -> List[str]: + """Create multiple records using the collection-bound ``CreateMultiple`` action. + + :param entity_set: Resolved entity set (plural) name. + :type entity_set: ``str`` + :param table_schema_name: Schema name of the table. + :type table_schema_name: ``str`` + :param records: Payload dictionaries mapped by column schema names. + :type records: ``list[dict[str, Any]]`` + + :return: List of created record GUIDs (may be empty if response lacks IDs). + :rtype: ``list[str]`` + + .. note:: + Logical type stamping: if any payload omits ``@odata.type`` the client injects ``Microsoft.Dynamics.CRM.``. If all payloads already include ``@odata.type`` no modification occurs. + """ + if not all(isinstance(r, dict) for r in records): + raise TypeError("All items for multi-create must be dicts") + r = await self._execute_raw(await self._build_create_multiple(entity_set, table_schema_name, records)) + try: + body = r.json() + except ValueError: + body = {} + if not isinstance(body, dict): + return [] + # Expected: { "Ids": [guid, ...] } + ids = body.get("Ids") + if isinstance(ids, list): + return [i for i in ids if isinstance(i, str)] + + value = body.get("value") + if isinstance(value, list): + # Extract IDs if possible + out: List[str] = [] + for item in value: + if isinstance(item, dict): + # Heuristic: look for a property ending with 'id' + for k, v in item.items(): + if isinstance(k, str) and k.lower().endswith("id") and isinstance(v, str) and len(v) >= 32: + out.append(v) + break + return out + return [] + + async def _upsert( + self, + entity_set: str, + table_schema_name: str, + alternate_key: Dict[str, Any], + record: Dict[str, Any], + ) -> None: + """Upsert a single record using an alternate key. + + Issues a PATCH request to ``{entity_set}({key_pairs})`` where ``key_pairs`` + is the OData alternate key segment built from ``alternate_key``. Creates the + record if it does not exist; updates it if it does. + + :param entity_set: Resolved entity set (plural) name. + :type entity_set: ``str`` + :param table_schema_name: Schema name of the table. + :type table_schema_name: ``str`` + :param alternate_key: Mapping of alternate key attribute names to their values + used to identify the target record in the URL. + :type alternate_key: ``dict[str, Any]`` + :param record: Attribute payload to set on the record. + :type record: ``dict[str, Any]`` + + :return: ``None`` + :rtype: ``None`` + """ + record = self._lowercase_keys(record) + record = await self._convert_labels_to_ints(table_schema_name, record) + key_str = self._build_alternate_key_str(alternate_key) + url = f"{self.api}/{entity_set}({key_str})" + await self._request("patch", url, json=record, expected=(200, 201, 204)) + + async def _upsert_multiple( + self, + entity_set: str, + table_schema_name: str, + alternate_keys: List[Dict[str, Any]], + records: List[Dict[str, Any]], + ) -> None: + """Upsert multiple records using the collection-bound ``UpsertMultiple`` action. + + Each target is formed by merging the corresponding alternate key fields and record + fields. The ``@odata.type`` annotation is injected automatically if absent. + + :param entity_set: Resolved entity set (plural) name. + :type entity_set: ``str`` + :param table_schema_name: Schema name of the table. + :type table_schema_name: ``str`` + :param alternate_keys: List of alternate key dictionaries, one per record. + Order is significant: ``alternate_keys[i]`` must correspond to ``records[i]``. + Python ``list`` preserves insertion order, so the correspondence is guaranteed + as long as both lists are built from the same source in the same order. + :type alternate_keys: ``list[dict[str, Any]]`` + :param records: List of record payload dictionaries, one per record. + Must be the same length as ``alternate_keys``. + :type records: ``list[dict[str, Any]]`` + + :return: ``None`` + :rtype: ``None`` + + :raises ValueError: If ``alternate_keys`` and ``records`` differ in length, or if + any record payload contains an alternate key field with a conflicting value. + """ + if len(alternate_keys) != len(records): + raise ValueError( + f"alternate_keys and records must have the same length " f"({len(alternate_keys)} != {len(records)})" + ) + logical_name = table_schema_name.lower() + lowered_records = [self._lowercase_keys(r) for r in records] + converted = [await self._convert_labels_to_ints(table_schema_name, r) for r in lowered_records] + targets: List[Dict[str, Any]] = [] + for alt_key, record_processed in zip(alternate_keys, converted): + alt_key_lower = self._lowercase_keys(alt_key) + conflicting = { + k for k in set(alt_key_lower) & set(record_processed) if alt_key_lower[k] != record_processed[k] + } + if conflicting: + raise ValueError(f"record payload conflicts with alternate_key on fields: {sorted(conflicting)!r}") + if "@odata.type" not in record_processed: + record_processed["@odata.type"] = f"Microsoft.Dynamics.CRM.{logical_name}" + key_str = self._build_alternate_key_str(alt_key) + record_processed["@odata.id"] = f"{entity_set}({key_str})" + targets.append(record_processed) + payload = {"Targets": targets} + url = f"{self.api}/{entity_set}/Microsoft.Dynamics.CRM.UpsertMultiple" + await self._request("post", url, json=payload, expected=(200, 201, 204)) + + # --- Derived helpers for high-level client ergonomics --- + async def _primary_id_attr(self, table_schema_name: str) -> str: + """Return primary key attribute using metadata; error if unavailable.""" + cache_key = self._normalize_cache_key(table_schema_name) + pid = self._logical_primaryid_cache.get(cache_key) + if pid: + return pid + # Resolve metadata (populates _logical_primaryid_cache or raises if table_schema_name unknown) + await self._entity_set_from_schema_name(table_schema_name) + pid2 = self._logical_primaryid_cache.get(cache_key) + if pid2: + return pid2 + raise RuntimeError( + f"PrimaryIdAttribute not resolved for table_schema_name '{table_schema_name}'. Metadata did not include PrimaryIdAttribute." + ) + + async def _update_by_ids( + self, + table_schema_name: str, + ids: List[str], + changes: Union[Dict[str, Any], List[Dict[str, Any]]], + ) -> None: + """Update many records by GUID list using the collection-bound ``UpdateMultiple`` action. + + :param table_schema_name: Schema name of the table. + :type table_schema_name: ``str`` + :param ids: GUIDs of target records. + :type ids: ``list[str]`` + :param changes: Broadcast patch (``dict``) applied to all IDs, or list of per-record patches (1:1 with ``ids``). + :type changes: ``dict`` | ``list[dict]`` + + :return: ``None`` + :rtype: ``None`` + """ + if not isinstance(ids, list): + raise TypeError("ids must be list[str]") + if not ids: + return None + pk_attr = await self._primary_id_attr(table_schema_name) + entity_set = await self._entity_set_from_schema_name(table_schema_name) + if isinstance(changes, dict): + batch = [{pk_attr: rid, **changes} for rid in ids] + await self._update_multiple(entity_set, table_schema_name, batch) + return + if not isinstance(changes, list): + raise TypeError("changes must be dict or list[dict]") + if len(changes) != len(ids): + raise ValueError("Length of changes list must match length of ids list") + batch: List[Dict[str, Any]] = [] + for rid, patch in zip(ids, changes): + if not isinstance(patch, dict): + raise TypeError("Each patch must be a dict") + batch.append({pk_attr: rid, **patch}) + await self._update_multiple(entity_set, table_schema_name, batch) + + async def _delete_multiple( + self, + table_schema_name: str, + ids: List[str], + ) -> Optional[str]: + """Delete many records by GUID list via the ``BulkDelete`` action. + + :param table_schema_name: Schema name of the table. + :type table_schema_name: ``str`` + :param ids: GUIDs of records to delete. + :type ids: ``list[str]`` + + :return: BulkDelete asynchronous job identifier when executed in bulk; ``None`` if no IDs provided or single deletes performed. + :rtype: ``str`` | ``None`` + """ + targets = [rid for rid in ids if rid] + if not targets: + return None + response = await self._execute_raw( + await self._build_delete_multiple(table_schema_name, targets), + expected=(200, 202, 204), + ) + job_id = None + try: + body = response.json() + except ValueError: + body = {} + if isinstance(body, dict): + job_id = body.get("JobId") + return job_id + + async def _update(self, table_schema_name: str, key: str, data: Dict[str, Any]) -> None: + """Update an existing record by GUID. + + :param table_schema_name: Schema name of the table. + :type table_schema_name: ``str`` + :param key: Record GUID (with or without parentheses). + :type key: ``str`` + :param data: Partial entity payload (attributes to patch). + :type data: ``dict[str, Any]`` + :return: ``None`` + :rtype: ``None`` + """ + await self._execute_raw(await self._build_update(table_schema_name, key, data)) + + async def _update_multiple( + self, + entity_set: str, + table_schema_name: str, + records: List[Dict[str, Any]], + ) -> None: + """Bulk update existing records via the collection-bound ``UpdateMultiple`` action. + + :param entity_set: Resolved entity set (plural) name. + :type entity_set: ``str`` + :param table_schema_name: Schema name of the table, e.g. "new_MyTestTable". + :type table_schema_name: ``str`` + :param records: List of patch dictionaries. Each must include the true primary key attribute (e.g. ``accountid``) and one or more fields to update. + :type records: ``list[dict[str, Any]]`` + :return: ``None`` + :rtype: ``None`` + + .. note:: + - Endpoint: ``POST /{entity_set}/Microsoft.Dynamics.CRM.UpdateMultiple`` with body ``{"Targets": [...]}``. + - Transactional semantics: if any individual update fails, the entire request rolls back. + - Response content is ignored; no stable contract for returned IDs/representations. + - Caller must supply the correct primary key attribute (e.g. ``accountid``) in every record. + """ + if not isinstance(records, list) or not records or not all(isinstance(r, dict) for r in records): + raise TypeError("records must be a non-empty list[dict]") + await self._execute_raw(await self._build_update_multiple_from_records(entity_set, table_schema_name, records)) + + async def _delete(self, table_schema_name: str, key: str) -> None: + """Delete a record by GUID. + + :param table_schema_name: Schema name of the table. + :type table_schema_name: ``str`` + :param key: Record GUID (with or without parentheses) + :type key: ``str`` + + :return: ``None`` + :rtype: ``None`` + """ + await self._execute_raw(await self._build_delete(table_schema_name, key)) + + async def _get( + self, + table_schema_name: str, + key: str, + select: Optional[List[str]] = None, + expand: Optional[List[str]] = None, + include_annotations: Optional[str] = None, + ) -> Dict[str, Any]: + """Retrieve a single record. + + :param table_schema_name: Schema name of the table. + :type table_schema_name: ``str`` + :param key: Record GUID (with or without parentheses). + :type key: ``str`` + :param select: Columns to select; joined with commas into $select. + :type select: ``list[str]`` | ``None`` + :param expand: Navigation properties to expand (``$expand``); passed as-is (case-sensitive). + :type expand: ``list[str]`` | ``None`` + :param include_annotations: OData annotation pattern for the ``Prefer: odata.include-annotations`` header, or ``None``. + :type include_annotations: ``str`` | ``None`` + + :return: Retrieved record dictionary (may be empty if no selected attributes). + :rtype: ``dict[str, Any]`` + """ + r = await self._execute_raw( + await self._build_get( + table_schema_name, key, select=select, expand=expand, include_annotations=include_annotations + ) + ) + return r.json() + + async def _get_multiple( + self, + table_schema_name: str, + select: Optional[List[str]] = None, + filter: Optional[str] = None, + orderby: Optional[List[str]] = None, + top: Optional[int] = None, + expand: Optional[List[str]] = None, + page_size: Optional[int] = None, + count: bool = False, + include_annotations: Optional[str] = None, + ) -> AsyncIterator[List[Dict[str, Any]]]: + """Iterate records from an entity set, yielding one page (list of dicts) at a time. + + :param table_schema_name: Schema name of the table. + :type table_schema_name: ``str`` + :param select: Columns to include (``$select``) or ``None``. Column names are automatically lowercased. + :type select: ``list[str]`` | ``None`` + :param filter: OData ``$filter`` expression or ``None``. This is passed as-is without transformation. Users must provide lowercase logical column names (e.g., "statecode eq 0"). + :type filter: ``str`` | ``None`` + :param orderby: Order expressions (``$orderby``) or ``None``. Column names are automatically lowercased. + :type orderby: ``list[str]`` | ``None`` + :param top: Max total records (applied on first request as ``$top``) or ``None``. + :type top: ``int`` | ``None`` + :param expand: Navigation properties to expand (``$expand``) or ``None``. These are case-sensitive and passed as-is. Users must provide exact navigation property names from entity metadata. + :type expand: ``list[str]`` | ``None`` + :param page_size: Per-page size hint via ``Prefer: odata.maxpagesize``. + :type page_size: ``int`` | ``None`` + :param count: If ``True``, adds ``$count=true`` to include a total record count in the response. + :type count: ``bool`` + :param include_annotations: OData annotation pattern for the ``Prefer: odata.include-annotations`` header (e.g. ``"*"`` or ``"OData.Community.Display.V1.FormattedValue"``), or ``None``. + :type include_annotations: ``str`` | ``None`` + + :return: Async iterator yielding pages (each page is a ``list`` of record dicts). + :rtype: ``AsyncIterator[list[dict[str, Any]]]`` + """ + extra_headers: Dict[str, str] = {} + prefer_parts: List[str] = [] + if page_size is not None: + ps = int(page_size) + if ps > 0: + prefer_parts.append(f"odata.maxpagesize={ps}") + if include_annotations: + prefer_parts.append(f'odata.include-annotations="{include_annotations}"') + if prefer_parts: + extra_headers["Prefer"] = ",".join(prefer_parts) + + async def _do_request(url: str, *, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]: + headers = extra_headers if extra_headers else None + r = await self._request("get", url, headers=headers, params=params) + try: + return r.json() + except ValueError: + return {} + + entity_set = await self._entity_set_from_schema_name(table_schema_name) + base_url = f"{self.api}/{entity_set}" + params: Dict[str, Any] = {} + if select: + # Lowercase column names for case-insensitive matching + params["$select"] = ",".join(self._lowercase_list(select)) + if filter: + # Filter is passed as-is; users must use lowercase column names in filter expressions + params["$filter"] = filter + if orderby: + # Lowercase column names for case-insensitive matching + params["$orderby"] = ",".join(self._lowercase_list(orderby)) + if expand: + # Lowercase navigation property names for case-insensitive matching + params["$expand"] = ",".join(expand) + if top is not None: + params["$top"] = int(top) + if count: + params["$count"] = "true" + + data = await _do_request(base_url, params=params) + items = data.get("value") if isinstance(data, dict) else None + if isinstance(items, list) and items: + yield [x for x in items if isinstance(x, dict)] + + next_link = None + if isinstance(data, dict): + next_link = data.get("@odata.nextLink") or data.get("odata.nextLink") + + while next_link: + data = await _do_request(next_link) + items = data.get("value") if isinstance(data, dict) else None + if isinstance(items, list) and items: + yield [x for x in items if isinstance(x, dict)] + next_link = data.get("@odata.nextLink") or data.get("odata.nextLink") if isinstance(data, dict) else None + + # --------------------------- SQL Custom API ------------------------- + async def _query_sql(self, sql: str) -> list[dict[str, Any]]: + """Execute a read-only SQL SELECT using the Dataverse Web API ``?sql=`` capability. + + :param sql: Single SELECT statement within the supported subset. + :type sql: ``str`` + + :return: Result rows (empty list if none). + :rtype: ``list[dict[str, Any]]`` + + :raises ValidationError: If ``sql`` is not a ``str`` or is empty. + :raises MetadataError: If logical table name resolution fails. + + .. note:: + Endpoint form: ``GET /{entity_set}?sql=``. The client + extracts the logical table name, resolves the entity set (metadata + cached), then issues the request. ``SELECT *`` raises + :class:`~PowerPlatform.Dataverse.core.errors.ValidationError` -- + it is deliberately rejected, not silently rewritten. + """ + if not isinstance(sql, str): + raise ValidationError("sql must be a string", subcode=VALIDATION_SQL_NOT_STRING) + if not sql.strip(): + raise ValidationError("sql must be a non-empty string", subcode=VALIDATION_SQL_EMPTY) + sql = sql.strip() + + # Apply safety guardrails (block unsupported syntax including writes, + # warn on risky patterns). SELECT * raises ValidationError here before + # any table resolution. + sql = self._sql_guardrails(sql) + + r = await self._execute_raw(await self._build_sql(sql)) + try: + body = r.json() + except ValueError: + return [] + + # Collect first page + results: list[dict[str, Any]] = [] + if isinstance(body, list): + return [row for row in body if isinstance(row, dict)] + if not isinstance(body, dict): + return results + + value = body.get("value") + if isinstance(value, list): + results = [row for row in value if isinstance(row, dict)] + + # Follow pagination links until exhausted + raw_link = body.get("@odata.nextLink") or body.get("odata.nextLink") + next_link: str | None = raw_link if isinstance(raw_link, str) else None + visited: set[str] = set() + seen_cookies: set[str] = set() + while next_link: + # Guard 1: exact URL cycle (same next_link returned twice) + if next_link in visited: + warnings.warn( + f"SQL pagination stopped after {len(results)} rows — " + "the Dataverse server returned the same nextLink URL twice, " + "indicating an infinite pagination cycle. " + "Returning the rows collected so far. " + "To avoid pagination entirely, add a TOP clause to your query.", + RuntimeWarning, + stacklevel=4, + ) + break + visited.add(next_link) + # Guard 2: server-side bug where pagingcookie does not advance between + # pages (pagenumber increments but cookie GUIDs stay the same), which + # causes an infinite loop even though URLs differ. + cookie = _extract_pagingcookie(next_link) + if cookie is not None: + if cookie in seen_cookies: + warnings.warn( + f"SQL pagination stopped after {len(results)} rows — " + "the Dataverse server returned the same pagingcookie twice " + "(pagenumber incremented but the paging position did not advance). " + "This is a server-side bug. Returning the rows collected so far. " + "To avoid pagination entirely, add a TOP clause to your query.", + RuntimeWarning, + stacklevel=4, + ) + break + seen_cookies.add(cookie) + try: + page_resp = await self._request("get", next_link) + except Exception as exc: + warnings.warn( + f"SQL pagination stopped after {len(results)} rows — " + f"the next-page request failed: {exc}. " + "Add a TOP clause to your query to limit results to a single page.", + RuntimeWarning, + stacklevel=5, + ) + break + try: + page_body = page_resp.json() + except ValueError as exc: + warnings.warn( + f"SQL pagination stopped after {len(results)} rows — " + f"the next-page response was not valid JSON: {exc}. " + "Add a TOP clause to your query to limit results to a single page.", + RuntimeWarning, + stacklevel=5, + ) + break + if not isinstance(page_body, dict): + break + page_value = page_body.get("value") + if not isinstance(page_value, list) or not page_value: + break + results.extend(row for row in page_value if isinstance(row, dict)) + raw_link = page_body.get("@odata.nextLink") or page_body.get("odata.nextLink") + next_link = raw_link if isinstance(raw_link, str) else None + + return results + + # ---------------------- Entity set resolution ----------------------- + async def _entity_set_from_schema_name(self, table_schema_name: str) -> str: + """Resolve entity set name (plural) from a schema name (singular) name using metadata. + + Caches results for subsequent queries. Case-insensitive. + """ + if not table_schema_name: + raise ValueError("table schema name required") + + # Use normalized (lowercase) key for cache lookup + cache_key = self._normalize_cache_key(table_schema_name) + cached = self._logical_to_entityset_cache.get(cache_key) + if cached: + return cached + url = f"{self.api}/EntityDefinitions" + # LogicalName in Dataverse is stored in lowercase, so we need to lowercase for the filter + logical_lower = table_schema_name.lower() + logical_escaped = self._escape_odata_quotes(logical_lower) + params = { + "$select": "LogicalName,EntitySetName,PrimaryIdAttribute", + "$filter": f"LogicalName eq '{logical_escaped}'", + } + r = await self._request("get", url, params=params) + try: + body = r.json() + items = body.get("value", []) if isinstance(body, dict) else [] + except ValueError: + items = [] + if not items: + plural_hint = ( + " (did you pass a plural entity set name instead of the singular table schema name?)" + if table_schema_name.endswith("s") and not table_schema_name.endswith("ss") + else "" + ) + raise MetadataError( + f"Unable to resolve entity set for table schema name '{table_schema_name}'. Provide the singular table schema name.{plural_hint}", + subcode=METADATA_ENTITYSET_NOT_FOUND, + ) + md = items[0] + es = md.get("EntitySetName") + if not es: + raise MetadataError( + f"Metadata response missing EntitySetName for table schema name '{table_schema_name}'.", + subcode=METADATA_ENTITYSET_NAME_MISSING, + ) + self._logical_to_entityset_cache[cache_key] = es + primary_id_attr = md.get("PrimaryIdAttribute") + if isinstance(primary_id_attr, str) and primary_id_attr: + self._logical_primaryid_cache[cache_key] = primary_id_attr + return es + + # ---------------------- Table metadata helpers ---------------------- + async def _get_entity_by_table_schema_name( + self, + table_schema_name: str, + headers: Optional[Dict[str, str]] = None, + ) -> Optional[Dict[str, Any]]: + """Get entity metadata by table schema name. Case-insensitive. + + Note: LogicalName is stored lowercase in Dataverse, so we lowercase the input + for case-insensitive matching. The response includes SchemaName, LogicalName, + EntitySetName, and MetadataId. + """ + url = f"{self.api}/EntityDefinitions" + # LogicalName is stored lowercase, so we lowercase the input for lookup + logical_lower = table_schema_name.lower() + logical_escaped = self._escape_odata_quotes(logical_lower) + params = { + "$select": "MetadataId,LogicalName,SchemaName,EntitySetName,PrimaryNameAttribute,PrimaryIdAttribute", + "$filter": f"LogicalName eq '{logical_escaped}'", + } + r = await self._request("get", url, params=params, headers=headers) + items = (r.json()).get("value", []) + return items[0] if items else None + + async def _create_entity( + self, + table_schema_name: str, + display_name: str, + attributes: List[Dict[str, Any]], + solution_unique_name: Optional[str] = None, + ) -> Dict[str, Any]: + url = f"{self.api}/EntityDefinitions" + payload = { + "@odata.type": "Microsoft.Dynamics.CRM.EntityMetadata", + "SchemaName": table_schema_name, + "DisplayName": self._label(display_name), + "DisplayCollectionName": self._label(display_name + "s"), + "Description": self._label(f"Custom entity for {display_name}"), + "OwnershipType": "UserOwned", + "HasActivities": False, + "HasNotes": True, + "IsActivity": False, + "Attributes": attributes, + } + params = None + if solution_unique_name: + params = {"SolutionUniqueName": solution_unique_name} + await self._request("post", url, json=payload, params=params) + ent = await self._get_entity_by_table_schema_name( + table_schema_name, + headers={"Consistency": "Strong"}, + ) + if not ent or not ent.get("EntitySetName"): + raise RuntimeError( + f"Failed to create or retrieve entity '{table_schema_name}' (EntitySetName not available)." + ) + if not ent.get("MetadataId"): + raise RuntimeError(f"MetadataId missing after creating entity '{table_schema_name}'.") + return ent + + async def _get_attribute_metadata( + self, + entity_metadata_id: str, + column_name: str, + extra_select: Optional[str] = None, + ) -> Optional[Dict[str, Any]]: + # Convert to lowercase logical name for lookup + logical_name = column_name.lower() + attr_escaped = self._escape_odata_quotes(logical_name) + url = f"{self.api}/EntityDefinitions({entity_metadata_id})/Attributes" + select_fields = ["MetadataId", "LogicalName", "SchemaName"] + if extra_select: + for piece in extra_select.split(","): + piece = piece.strip() + if not piece or piece in select_fields: + continue + if piece.startswith("@"): + continue + if piece not in select_fields: + select_fields.append(piece) + params = { + "$select": ",".join(select_fields), + "$filter": f"LogicalName eq '{attr_escaped}'", + } + r = await self._request("get", url, params=params) + try: + body = r.json() + except ValueError: + return None + items = body.get("value") if isinstance(body, dict) else None + if isinstance(items, list) and items: + item = items[0] + if isinstance(item, dict): + return item + return None + + async def _list_columns( + self, + table_schema_name: str, + *, + select: Optional[List[str]] = None, + filter: Optional[str] = None, + ) -> List[Dict[str, Any]]: + """List all attribute (column) definitions for a table. + + Issues ``GET EntityDefinitions({MetadataId})/Attributes`` with optional + ``$select`` and ``$filter`` query parameters. + + :param table_schema_name: Schema name of the table + (e.g. ``"account"`` or ``"new_Product"``). + :type table_schema_name: ``str`` + :param select: Optional list of property names to project via + ``$select``. Values are passed as-is (PascalCase). + :type select: ``list[str]`` or ``None`` + :param filter: Optional OData ``$filter`` expression. For example, + ``"AttributeType eq 'String'"`` returns only string columns. + :type filter: ``str`` or ``None`` + + :return: List of raw attribute metadata dictionaries (may be empty). + :rtype: ``list[dict[str, Any]]`` + + :raises MetadataError: If the table is not found. + :raises HttpError: If the Web API request fails. + """ + ent = await self._get_entity_by_table_schema_name(table_schema_name) + if not ent or not ent.get("MetadataId"): + raise MetadataError( + f"Table '{table_schema_name}' not found.", + subcode=METADATA_TABLE_NOT_FOUND, + ) + metadata_id = ent["MetadataId"] + url = f"{self.api}/EntityDefinitions({metadata_id})/Attributes" + params: Dict[str, str] = {} + if select: + params["$select"] = ",".join(select) + if filter: + params["$filter"] = filter + r = await self._request("get", url, params=params) + return (r.json()).get("value", []) + + async def _wait_for_attribute_visibility( + self, + entity_set: str, + attribute_name: str, + delays: tuple = (0, 3, 10, 20), + ) -> None: + """Wait for a newly created attribute to become visible in the data API. + + After creating an attribute via the metadata API, there can be a delay before + it becomes queryable in the data API. This method polls the entity set with + the attribute in the $select clause until it succeeds or all delays are exhausted. + """ + # Convert to lowercase logical name for URL + logical_name = attribute_name.lower() + probe_url = f"{self.api}/{entity_set}?$top=1&$select={logical_name}" + last_error = None + total_wait = sum(delays) + + for delay in delays: + if delay: + await asyncio.sleep(delay) + try: + await self._request("get", probe_url) + return + except Exception as ex: + last_error = ex + continue + + # All retries exhausted - raise with context + raise RuntimeError( + f"Attribute '{logical_name}' did not become visible in the data API " + f"after {total_wait} seconds (exhausted all retries)." + ) from last_error + + async def _request_metadata_with_retry(self, method: str, url: str, **kwargs) -> _AsyncResponse: + """Fetch metadata with retries on transient errors.""" + max_attempts = 5 + backoff_seconds = 0.4 + for attempt in range(1, max_attempts + 1): + try: + return await self._request(method, url, **kwargs) + except HttpError as err: + if getattr(err, "status_code", None) == 404: + if attempt < max_attempts: + await asyncio.sleep(backoff_seconds * (2 ** (attempt - 1))) + continue + raise RuntimeError(f"Metadata request failed after {max_attempts} retries (404): {url}") from err + raise + + async def _bulk_fetch_picklists(self, table_schema_name: str) -> None: + """Fetch all picklist attributes and their options for a table in one API call. + + Uses collection-level PicklistAttributeMetadata cast to retrieve every picklist + attribute on the table, including its OptionSet options. Populates the nested + cache so that ``_convert_labels_to_ints`` resolves labels without further API calls. + The Dataverse metadata API does not page results. + """ + table_key = self._normalize_cache_key(table_schema_name) + + # Fast path: skip the lock when the cache is already warm. + now = time.time() + table_entry = self._picklist_label_cache.get(table_key) + if isinstance(table_entry, dict) and (now - table_entry.get("ts", 0)) < self._picklist_cache_ttl_seconds: + return + + # Slow path: acquire the lock so that only one coroutine issues the metadata + # fetch. The second TTL check inside the lock handles the case where another + # coroutine populated the cache while we were waiting. + async with self._picklist_cache_lock: + now = time.time() + table_entry = self._picklist_label_cache.get(table_key) + if isinstance(table_entry, dict) and (now - table_entry.get("ts", 0)) < self._picklist_cache_ttl_seconds: + return + + table_esc = self._escape_odata_quotes(table_schema_name.lower()) + url = ( + f"{self.api}/EntityDefinitions(LogicalName='{table_esc}')" + f"/Attributes/Microsoft.Dynamics.CRM.PicklistAttributeMetadata" + f"?$select=LogicalName&$expand=OptionSet($select=Options)" + ) + response = await self._request_metadata_with_retry("get", url) + body = response.json() + items = body.get("value", []) if isinstance(body, dict) else [] + + picklists: Dict[str, Dict[str, int]] = {} + for item in items: + if not isinstance(item, dict): + continue + ln = item.get("LogicalName", "").lower() + if not ln: + continue + option_set = item.get("OptionSet") or {} + options = option_set.get("Options") if isinstance(option_set, dict) else None + mapping: Dict[str, int] = {} + if isinstance(options, list): + for opt in options: + if not isinstance(opt, dict): + continue + val = opt.get("Value") + if not isinstance(val, int): + continue + label_def = opt.get("Label") or {} + locs = label_def.get("LocalizedLabels") + if isinstance(locs, list): + for loc in locs: + if isinstance(loc, dict): + lab = loc.get("Label") + if isinstance(lab, str) and lab.strip(): + normalized = self._normalize_picklist_label(lab) + mapping.setdefault(normalized, val) + picklists[ln] = mapping + + self._picklist_label_cache[table_key] = {"ts": now, "picklists": picklists} + + async def _convert_labels_to_ints(self, table_schema_name: str, record: Dict[str, Any]) -> Dict[str, Any]: + """Return a copy of record with any labels converted to option ints. + + Heuristic: For each string value, attempt to resolve against picklist metadata. + If attribute isn't a picklist or label not found, value left unchanged. + + On first encounter of a table, bulk-fetches all picklist attributes and + their options in a single API call, then resolves labels from the warm cache. + """ + resolved_record = record.copy() + + # Check if there are any string-valued candidates worth resolving + has_candidates = any( + isinstance(v, str) and v.strip() and isinstance(k, str) and "@odata." not in k + for k, v in resolved_record.items() + ) + if not has_candidates: + return resolved_record + + # Bulk-fetch all picklists for this table (1 API call, cached for TTL) + await self._bulk_fetch_picklists(table_schema_name) + + # Resolve labels from the nested cache + table_key = self._normalize_cache_key(table_schema_name) + table_entry = self._picklist_label_cache.get(table_key) + if not isinstance(table_entry, dict): + return resolved_record + picklists = table_entry.get("picklists", {}) + + for k, v in resolved_record.items(): + if not isinstance(v, str) or not v.strip(): + continue + if isinstance(k, str) and "@odata." in k: + continue + attr_key = self._normalize_cache_key(k) + mapping = picklists.get(attr_key) + if not isinstance(mapping, dict) or not mapping: + continue + norm = self._normalize_picklist_label(v) + val = mapping.get(norm) + if val is not None: + resolved_record[k] = val + return resolved_record + + async def _get_table_info(self, table_schema_name: str) -> Optional[Dict[str, Any]]: + """Return basic metadata for a custom table if it exists. + + :param table_schema_name: Schema name of the table. + :type table_schema_name: ``str`` + + :return: Metadata summary or ``None`` if not found. + :rtype: ``dict[str, Any]`` | ``None`` + """ + ent = await self._get_entity_by_table_schema_name(table_schema_name) + if not ent: + return None + return { + "table_schema_name": ent.get("SchemaName") or table_schema_name, + "table_logical_name": ent.get("LogicalName"), + "entity_set_name": ent.get("EntitySetName"), + "metadata_id": ent.get("MetadataId"), + "primary_name_attribute": ent.get("PrimaryNameAttribute"), + "primary_id_attribute": ent.get("PrimaryIdAttribute"), + "columns_created": [], + } + + async def _list_tables( + self, + filter: Optional[str] = None, + select: Optional[List[str]] = None, + ) -> List[Dict[str, Any]]: + """List all non-private tables (``IsPrivate eq false``). + + :param filter: Optional additional OData ``$filter`` expression that is + combined with the default ``IsPrivate eq false`` clause using + ``and``. For example, ``"SchemaName eq 'Account'"`` becomes + ``"IsPrivate eq false and (SchemaName eq 'Account')"``. + When ``None`` (the default), only the ``IsPrivate eq false`` filter + is applied. + :type filter: ``str`` or ``None`` + :param select: Optional list of property names to project via + ``$select``. Values are passed as-is (PascalCase) because + ``EntityDefinitions`` uses PascalCase property names. + When ``None`` (the default) or an empty list, no ``$select`` is + applied and all properties are returned. Passing a bare string + raises ``TypeError``. + :type select: ``list[str]`` or ``None`` + + :return: Metadata entries for non-private tables (may be empty). + :rtype: ``list[dict[str, Any]]`` + + :raises HttpError: If the metadata request fails. + """ + r = await self._execute_raw(self._build_list_entities(filter=filter, select=select)) + return (r.json()).get("value", []) + + async def _delete_table(self, table_schema_name: str) -> None: + """Delete a table by schema name. + + :param table_schema_name: Schema name of the table. + :type table_schema_name: ``str`` + + :return: ``None`` + :rtype: ``None`` + + :raises MetadataError: If the table does not exist. + :raises HttpError: If the delete request fails. + """ + ent = await self._get_entity_by_table_schema_name(table_schema_name) + if not ent or not ent.get("MetadataId"): + raise MetadataError( + f"Table '{table_schema_name}' not found.", + subcode=METADATA_TABLE_NOT_FOUND, + ) + await self._execute_raw(self._build_delete_entity(ent["MetadataId"])) + + # ------------------- Alternate key metadata helpers ------------------- + + async def _create_alternate_key( + self, + table_schema_name: str, + key_name: str, + columns: List[str], + display_name_label=None, + ) -> Dict[str, Any]: + """Create an alternate key on a table. + + Issues ``POST EntityDefinitions(LogicalName='{logical_name}')/Keys`` + with ``EntityKeyMetadata`` payload. + + :param table_schema_name: Schema name of the table. + :type table_schema_name: ``str`` + :param key_name: Schema name for the new alternate key. + :type key_name: ``str`` + :param columns: List of column logical names that compose the key. + :type columns: ``list[str]`` + :param display_name_label: Label for the key display name. + :type display_name_label: ``Label`` or ``None`` + + :return: Dictionary with ``metadata_id``, ``schema_name``, and ``key_attributes``. + :rtype: ``dict[str, Any]`` + + :raises MetadataError: If the table does not exist. + :raises HttpError: If the Web API request fails. + """ + ent = await self._get_entity_by_table_schema_name(table_schema_name) + if not ent or not ent.get("MetadataId"): + raise MetadataError( + f"Table '{table_schema_name}' not found.", + subcode=METADATA_TABLE_NOT_FOUND, + ) + + logical_name = ent.get("LogicalName", table_schema_name.lower()) + url = f"{self.api}/EntityDefinitions(LogicalName='{logical_name}')/Keys" + payload: Dict[str, Any] = { + "SchemaName": key_name, + "KeyAttributes": columns, + } + if display_name_label is not None: + payload["DisplayName"] = display_name_label.to_dict() + r = await self._request("post", url, json=payload) + metadata_id = self._extract_id_from_header(r.headers.get("OData-EntityId")) + + return { + "metadata_id": metadata_id, + "schema_name": key_name, + "key_attributes": columns, + } + + async def _get_alternate_keys(self, table_schema_name: str) -> List[Dict[str, Any]]: + """List all alternate keys on a table. + + Issues ``GET EntityDefinitions(LogicalName='{logical_name}')/Keys``. + + :param table_schema_name: Schema name of the table. + :type table_schema_name: ``str`` + + :return: List of raw ``EntityKeyMetadata`` dictionaries. + :rtype: ``list[dict[str, Any]]`` + + :raises MetadataError: If the table does not exist. + :raises HttpError: If the Web API request fails. + """ + ent = await self._get_entity_by_table_schema_name(table_schema_name) + if not ent or not ent.get("MetadataId"): + raise MetadataError( + f"Table '{table_schema_name}' not found.", + subcode=METADATA_TABLE_NOT_FOUND, + ) + + logical_name = ent.get("LogicalName", table_schema_name.lower()) + url = f"{self.api}/EntityDefinitions(LogicalName='{logical_name}')/Keys" + r = await self._request("get", url) + return (r.json()).get("value", []) + + async def _delete_alternate_key(self, table_schema_name: str, key_id: str) -> None: + """Delete an alternate key by metadata ID. + + Issues ``DELETE EntityDefinitions(LogicalName='{logical_name}')/Keys({key_id})``. + + :param table_schema_name: Schema name of the table. + :type table_schema_name: ``str`` + :param key_id: Metadata GUID of the alternate key. + :type key_id: ``str`` + + :return: ``None`` + :rtype: ``None`` + + :raises MetadataError: If the table does not exist. + :raises HttpError: If the Web API request fails. + """ + ent = await self._get_entity_by_table_schema_name(table_schema_name) + if not ent or not ent.get("MetadataId"): + raise MetadataError( + f"Table '{table_schema_name}' not found.", + subcode=METADATA_TABLE_NOT_FOUND, + ) + + logical_name = ent.get("LogicalName", table_schema_name.lower()) + url = f"{self.api}/EntityDefinitions(LogicalName='{logical_name}')/Keys({key_id})" + await self._request("delete", url) + + async def _create_table( + self, + table_schema_name: str, + schema: Dict[str, Any], + solution_unique_name: Optional[str] = None, + primary_column_schema_name: Optional[str] = None, + display_name: Optional[str] = None, + ) -> Dict[str, Any]: + """Create a custom table with specified columns. + + :param table_schema_name: Schema name of the table. + :type table_schema_name: ``str`` + :param schema: Mapping of column name -> type spec (``str`` or ``Enum`` subclass). + :type schema: ``dict[str, Any]`` + :param solution_unique_name: Optional solution container for the new table; if provided must be non-empty. + :type solution_unique_name: ``str`` | ``None`` + :param primary_column_schema_name: Optional primary column schema name. + :type primary_column_schema_name: ``str`` | ``None`` + :param display_name: Human-readable display name for the table. Defaults to ``table_schema_name``. + :type display_name: ``str`` | ``None`` + + :return: Metadata summary for the created table including created column schema names. + :rtype: ``dict[str, Any]`` + + :raises MetadataError: If the table already exists. + :raises ValueError: If a column type is unsupported or ``solution_unique_name`` is empty. + :raises TypeError: If ``solution_unique_name`` is not a ``str`` when provided. + :raises HttpError: If underlying HTTP requests fail. + """ + # Check if table already exists (case-insensitive) + ent = await self._get_entity_by_table_schema_name(table_schema_name) + if ent: + raise MetadataError( + f"Table '{table_schema_name}' already exists.", + subcode=METADATA_TABLE_ALREADY_EXISTS, + ) + + created_cols: List[str] = [] + + # Use provided primary column name, or derive from table_schema_name prefix (e.g., "new_Product" -> "new_Name"). + # If no prefix detected, default to "new_Name"; server will validate overall table schema. + if primary_column_schema_name: + primary_attr_schema = primary_column_schema_name + else: + primary_attr_schema = ( + f"{table_schema_name.split('_',1)[0]}_Name" if "_" in table_schema_name else "new_Name" + ) + + attributes: List[Dict[str, Any]] = [] + attributes.append(self._attribute_payload(primary_attr_schema, "string", is_primary_name=True)) + for col_name, dtype in schema.items(): + payload = self._attribute_payload(col_name, dtype) + if not payload: + raise ValueError(f"Unsupported column type '{dtype}' for '{col_name}'.") + attributes.append(payload) + created_cols.append(col_name) + + if solution_unique_name is not None: + if not isinstance(solution_unique_name, str): + raise TypeError("solution_unique_name must be a string when provided") + if not solution_unique_name: + raise ValueError("solution_unique_name cannot be empty") + + if display_name is not None: + if not isinstance(display_name, str) or not display_name.strip(): + raise TypeError("display_name must be a non-empty string when provided") + + metadata = await self._create_entity( + table_schema_name=table_schema_name, + display_name=display_name if display_name is not None else table_schema_name, + attributes=attributes, + solution_unique_name=solution_unique_name, + ) + + return { + "table_schema_name": table_schema_name, + "table_logical_name": metadata.get("LogicalName"), + "entity_set_name": metadata.get("EntitySetName"), + "metadata_id": metadata.get("MetadataId"), + "primary_name_attribute": metadata.get("PrimaryNameAttribute"), + "primary_id_attribute": metadata.get("PrimaryIdAttribute"), + "columns_created": created_cols, + } + + async def _create_columns( + self, + table_schema_name: str, + columns: Dict[str, Any], + ) -> List[str]: + """Create new columns on an existing table. + + :param table_schema_name: Schema name of the table. + :type table_schema_name: ``str`` + :param columns: Mapping of column schema name -> type spec (``str`` or ``Enum`` subclass). + :type columns: ``dict[str, Any]`` + + :return: List of created column schema names. + :rtype: ``list[str]`` + + :raises TypeError: If ``columns`` is not a non-empty dict. + :raises MetadataError: If the target table does not exist. + :raises ValueError: If a column type is unsupported. + :raises HttpError: If an underlying HTTP request fails. + """ + if not isinstance(columns, dict) or not columns: + raise TypeError("columns must be a non-empty dict[name -> type]") + + ent = await self._get_entity_by_table_schema_name(table_schema_name) + if not ent or not ent.get("MetadataId"): + raise MetadataError( + f"Table '{table_schema_name}' not found.", + subcode=METADATA_TABLE_NOT_FOUND, + ) + + metadata_id = ent.get("MetadataId") + created: List[str] = [] + needs_picklist_flush = False + + for column_name, column_type in columns.items(): + attr = self._attribute_payload(column_name, column_type) + if not attr: + raise ValidationError( + f"Unsupported column type '{column_type}' for column '{column_name}'.", + subcode=VALIDATION_UNSUPPORTED_COLUMN_TYPE, + ) + if "OptionSet" in attr: + needs_picklist_flush = True + req = _RawRequest( + method="POST", + url=f"{self.api}/EntityDefinitions({metadata_id})/Attributes", + body=json.dumps(attr, ensure_ascii=False), + ) + await self._execute_raw(req) + created.append(column_name) + + if needs_picklist_flush: + self._flush_cache("picklist") + + return created + + async def _delete_columns( + self, + table_schema_name: str, + columns: Union[str, List[str]], + ) -> List[str]: + """Delete one or more columns from a table. + + :param table_schema_name: Schema name of the table. + :type table_schema_name: ``str`` + :param columns: Single column name or list of column names + :type columns: ``str`` | ``list[str]`` + + :return: List of deleted column schema names (empty if none removed). + :rtype: ``list[str]`` + + :raises TypeError: If ``columns`` is neither a ``str`` nor ``list[str]``. + :raises ValueError: If any provided column name is empty. + :raises MetadataError: If the table or a specified column does not exist. + :raises RuntimeError: If column metadata lacks a required ``MetadataId``. + :raises HttpError: If an underlying delete request fails. + """ + if isinstance(columns, str): + names = [columns] + elif isinstance(columns, list): + names = columns + else: + raise TypeError("columns must be str or list[str]") + + for name in names: + if not isinstance(name, str) or not name.strip(): + raise ValueError("column names must be non-empty strings") + + ent = await self._get_entity_by_table_schema_name(table_schema_name) + if not ent or not ent.get("MetadataId"): + raise MetadataError( + f"Table '{table_schema_name}' not found.", + subcode=METADATA_TABLE_NOT_FOUND, + ) + + # Use the actual SchemaName from the entity metadata + entity_schema = ent.get("SchemaName") or table_schema_name + metadata_id = ent.get("MetadataId") + deleted: List[str] = [] + needs_picklist_flush = False + + attr_metas = [ + await self._get_attribute_metadata(metadata_id, col, extra_select="@odata.type,AttributeType") + for col in names + ] + for column_name, attr_meta in zip(names, attr_metas): + if not attr_meta: + raise MetadataError( + f"Column '{column_name}' not found on table '{entity_schema}'.", + subcode=METADATA_COLUMN_NOT_FOUND, + ) + + attr_metadata_id = attr_meta.get("MetadataId") + if not attr_metadata_id: + raise RuntimeError(f"Metadata incomplete for column '{column_name}' (missing MetadataId).") + + await self._execute_raw(self._build_delete_column(metadata_id, attr_metadata_id)) + + attr_type = attr_meta.get("@odata.type") or attr_meta.get("AttributeType") + if isinstance(attr_type, str): + attr_type_l = attr_type.lower() + if "picklist" in attr_type_l or "optionset" in attr_type_l: + needs_picklist_flush = True + + deleted.append(column_name) + + if needs_picklist_flush: + self._flush_cache("picklist") + + return deleted + + # ---------------------- _build_* methods (no HTTP, but may call async helpers) --------------- + + async def _build_create( + self, + entity_set: str, + table: str, + data: Dict[str, Any], + *, + content_id: Optional[int] = None, + ) -> _RawRequest: + """Build a single-record POST request without sending it.""" + body = self._lowercase_keys(data) + body = await self._convert_labels_to_ints(table, body) + return _RawRequest( + method="POST", + url=f"{self.api}/{entity_set}", + body=json.dumps(body, ensure_ascii=False), + content_id=content_id, + ) + + async def _build_create_multiple( + self, + entity_set: str, + table: str, + records: List[Dict[str, Any]], + ) -> _RawRequest: + """Build a CreateMultiple POST request without sending it.""" + if not all(isinstance(r, dict) for r in records): + raise TypeError("All items for multi-create must be dicts") + logical_name = table.lower() + lowered = [self._lowercase_keys(r) for r in records] + converted = [await self._convert_labels_to_ints(table, r) for r in lowered] + enriched = [ + {**r, "@odata.type": f"Microsoft.Dynamics.CRM.{logical_name}"} if "@odata.type" not in r else r + for r in converted + ] + return _RawRequest( + method="POST", + url=f"{self.api}/{entity_set}/Microsoft.Dynamics.CRM.CreateMultiple", + body=json.dumps({"Targets": enriched}, ensure_ascii=False), + ) + + async def _build_update( + self, + table: str, + record_id: str, + changes: Dict[str, Any], + *, + content_id: Optional[int] = None, + ) -> _RawRequest: + """Build a single-record PATCH request without sending it. + + ``record_id`` may be a ``"$n"`` content-ID reference; in that case the + URL is the reference itself (resolved server-side within a changeset). + """ + body = self._lowercase_keys(changes) + body = await self._convert_labels_to_ints(table, body) + if record_id.startswith("$"): + url = record_id + else: + entity_set = await self._entity_set_from_schema_name(table) + url = f"{self.api}/{entity_set}{self._format_key(record_id)}" + return _RawRequest( + method="PATCH", + url=url, + body=json.dumps(body, ensure_ascii=False), + headers={"If-Match": "*"}, + content_id=content_id, + ) + + async def _build_update_multiple_from_records( + self, + entity_set: str, + table: str, + records: List[Dict[str, Any]], + ) -> _RawRequest: + """Build an UpdateMultiple POST request from pre-assembled records. + + Each record must already contain the primary key attribute. This helper + is shared by :meth:`_update_multiple` (which pre-assembles records) and + :meth:`_build_update_multiple` (which assembles from ids + changes). + """ + logical_name = table.lower() + lowered = [self._lowercase_keys(r) for r in records] + converted = [await self._convert_labels_to_ints(table, r) for r in lowered] + enriched = [ + {**r, "@odata.type": f"Microsoft.Dynamics.CRM.{logical_name}"} if "@odata.type" not in r else r + for r in converted + ] + return _RawRequest( + method="POST", + url=f"{self.api}/{entity_set}/Microsoft.Dynamics.CRM.UpdateMultiple", + body=json.dumps({"Targets": enriched}, ensure_ascii=False), + ) + + async def _build_update_multiple( + self, + entity_set: str, + table: str, + ids: List[str], + changes: Union[Dict[str, Any], List[Dict[str, Any]]], + ) -> _RawRequest: + """Build an UpdateMultiple POST request without sending it.""" + pk_attr = await self._primary_id_attr(table) + if isinstance(changes, dict): + records = [{pk_attr: rid, **changes} for rid in ids] + elif isinstance(changes, list): + if len(changes) != len(ids): + raise ValidationError( + "ids and changes lists must have equal length for paired update.", + subcode="ids_changes_length_mismatch", + ) + records = [{pk_attr: rid, **ch} for rid, ch in zip(ids, changes)] + else: + raise ValidationError("changes must be a dict or list[dict].", subcode="invalid_changes_type") + return await self._build_update_multiple_from_records(entity_set, table, records) + + async def _build_upsert( + self, + entity_set: str, + table: str, + alternate_key: Dict[str, Any], + record: Dict[str, Any], + ) -> _RawRequest: + """Build a single-record PATCH upsert request without sending it. + + Unlike :meth:`_build_update`, no ``If-Match: *`` header is added so the + server creates the record when it does not yet exist. + """ + body = self._lowercase_keys(record) + body = await self._convert_labels_to_ints(table, body) + key_str = self._build_alternate_key_str(alternate_key) + url = f"{self.api}/{entity_set}({key_str})" + return _RawRequest( + method="PATCH", + url=url, + body=json.dumps(body, ensure_ascii=False), + ) + + async def _build_upsert_multiple( + self, + entity_set: str, + table: str, + alternate_keys: List[Dict[str, Any]], + records: List[Dict[str, Any]], + ) -> _RawRequest: + """Build an UpsertMultiple POST request without sending it.""" + if len(alternate_keys) != len(records): + raise ValidationError( + f"alternate_keys and records must have the same length " f"({len(alternate_keys)} != {len(records)})", + subcode="upsert_length_mismatch", + ) + logical_name = table.lower() + lowered_records = [self._lowercase_keys(r) for r in records] + converted = [await self._convert_labels_to_ints(table, r) for r in lowered_records] + targets: List[Dict[str, Any]] = [] + for alt_key, record_processed in zip(alternate_keys, converted): + alt_key_lower = self._lowercase_keys(alt_key) + conflicting = { + k for k in set(alt_key_lower) & set(record_processed) if alt_key_lower[k] != record_processed[k] + } + if conflicting: + raise ValidationError( + f"record payload conflicts with alternate_key on fields: {sorted(conflicting)!r}", + subcode="upsert_key_conflict", + ) + if "@odata.type" not in record_processed: + record_processed["@odata.type"] = f"Microsoft.Dynamics.CRM.{logical_name}" + key_str = self._build_alternate_key_str(alt_key) + record_processed["@odata.id"] = f"{entity_set}({key_str})" + targets.append(record_processed) + return _RawRequest( + method="POST", + url=f"{self.api}/{entity_set}/Microsoft.Dynamics.CRM.UpsertMultiple", + body=json.dumps({"Targets": targets}, ensure_ascii=False), + ) + + async def _build_delete( + self, + table: str, + record_id: str, + *, + content_id: Optional[int] = None, + ) -> _RawRequest: + """Build a single-record DELETE request without sending it. + + ``record_id`` may be a ``"$n"`` content-ID reference. + """ + if record_id.startswith("$"): + url = record_id + else: + entity_set = await self._entity_set_from_schema_name(table) + url = f"{self.api}/{entity_set}{self._format_key(record_id)}" + return _RawRequest( + method="DELETE", + url=url, + headers={"If-Match": "*"}, + content_id=content_id, + ) + + async def _build_delete_multiple(self, table: str, ids: List[str]) -> _RawRequest: + """Build a BulkDelete POST request without sending it.""" + pk_attr = await self._primary_id_attr(table) + logical_name = table.lower() + timestamp = datetime.now(timezone.utc).isoformat(timespec="seconds").replace("+00:00", "Z") + payload = { + "JobName": f"Bulk delete {table} records @ {timestamp}", + "SendEmailNotification": False, + "ToRecipients": [], + "CCRecipients": [], + "RecurrencePattern": "", + "StartDateTime": timestamp, + "QuerySet": [ + { + "@odata.type": "Microsoft.Dynamics.CRM.QueryExpression", + "EntityName": logical_name, + "ColumnSet": { + "@odata.type": "Microsoft.Dynamics.CRM.ColumnSet", + "AllColumns": False, + "Columns": [], + }, + "Criteria": { + "@odata.type": "Microsoft.Dynamics.CRM.FilterExpression", + "FilterOperator": "And", + "Conditions": [ + { + "@odata.type": "Microsoft.Dynamics.CRM.ConditionExpression", + "AttributeName": pk_attr, + "Operator": "In", + "Values": [{"Value": rid, "Type": "System.Guid"} for rid in ids], + } + ], + }, + } + ], + } + return _RawRequest( + method="POST", + url=f"{self.api}/BulkDelete", + body=json.dumps(payload, ensure_ascii=False), + ) + + async def _build_get( + self, + table: str, + record_id: str, + *, + select: Optional[List[str]] = None, + expand: Optional[List[str]] = None, + include_annotations: Optional[str] = None, + ) -> _RawRequest: + """Build a single-record GET request without sending it.""" + entity_set = await self._entity_set_from_schema_name(table) + params: List[str] = [] + if select: + params.append("$select=" + ",".join(self._lowercase_list(select))) + if expand: + params.append("$expand=" + ",".join(expand)) + url = f"{self.api}/{entity_set}{self._format_key(record_id)}" + if params: + url += "?" + "&".join(params) + headers = None + if include_annotations: + headers = {"Prefer": f'odata.include-annotations="{include_annotations}"'} + return _RawRequest(method="GET", url=url, headers=headers) + + async def _build_list( + self, + table: str, + *, + select: Optional[List[str]] = None, + filter: Optional[str] = None, + orderby: Optional[List[str]] = None, + top: Optional[int] = None, + expand: Optional[List[str]] = None, + page_size: Optional[int] = None, + count: bool = False, + include_annotations: Optional[str] = None, + ) -> _RawRequest: + """Build a multi-record GET request (single page, no pagination) without sending it.""" + entity_set = await self._entity_set_from_schema_name(table) + params: List[str] = [] + if select: + params.append("$select=" + ",".join(self._lowercase_list(select))) + if filter: + params.append("$filter=" + filter) + if orderby: + params.append("$orderby=" + ",".join(orderby)) + if top is not None: + params.append(f"$top={top}") + if expand: + params.append("$expand=" + ",".join(expand)) + if count: + params.append("$count=true") + url = f"{self.api}/{entity_set}" + if params: + url += "?" + "&".join(params) + prefer_parts: List[str] = [] + if page_size is not None: + ps = int(page_size) + if ps > 0: + prefer_parts.append(f"odata.maxpagesize={ps}") + if include_annotations: + prefer_parts.append(f'odata.include-annotations="{include_annotations}"') + headers = {"Prefer": ",".join(prefer_parts)} if prefer_parts else None + return _RawRequest(method="GET", url=url, headers=headers) + + async def _build_sql(self, sql: str) -> _RawRequest: + """Build a SQL query GET request without sending it. + + Resolves the entity set from the table name in the SQL statement via + :meth:`_extract_logical_table`, then embeds the SQL as a URL-encoded + ``?sql=`` query parameter. + + Uses ``urllib.parse.quote`` (``%20`` for spaces) rather than + ``urllib.parse.urlencode`` (``+`` for spaces). Both are accepted by + Dataverse and ``%20`` is the canonical RFC 3986 encoding for query- + string values. + + :param sql: SELECT statement (non-empty string; caller is responsible + for validation). + """ + logical = self._extract_logical_table(sql) + entity_set = await self._entity_set_from_schema_name(logical) + return _RawRequest( + method="GET", + url=f"{self.api}/{entity_set}?sql={_url_quote(sql, safe='')}", + ) diff --git a/src/PowerPlatform/Dataverse/aio/data/_async_relationships.py b/src/PowerPlatform/Dataverse/aio/data/_async_relationships.py new file mode 100644 index 00000000..cbeac29a --- /dev/null +++ b/src/PowerPlatform/Dataverse/aio/data/_async_relationships.py @@ -0,0 +1,263 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +""" +Async relationship metadata operations for Dataverse Web API. + +This module provides mixin functionality for relationship CRUD operations. +""" + +from __future__ import annotations + +__all__ = [] + +import asyncio +import re +from typing import Any, Dict, List, Optional + +from ...core.errors import MetadataError +from ...core._error_codes import METADATA_TABLE_NOT_FOUND + + +class _AsyncRelationshipOperationsMixin: + """ + Async mixin providing relationship metadata operations. + + This mixin is designed to be used with _AsyncODataClient and depends on: + - self.api: The API base URL + - self._headers(): Method to get auth headers + - self._request(): Async method to make HTTP requests + """ + + async def _create_one_to_many_relationship( + self, + lookup, + relationship, + solution: Optional[str] = None, + ) -> Dict[str, Any]: + """ + Create a one-to-many relationship with lookup attribute. + + Posts to /RelationshipDefinitions with OneToManyRelationshipMetadata. + + :param lookup: Lookup attribute metadata (LookupAttributeMetadata instance). + :type lookup: ~PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata + :param relationship: Relationship metadata (OneToManyRelationshipMetadata instance). + :type relationship: ~PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata + :param solution: Optional solution unique name to add the relationship to. + :type solution: ``str`` | ``None`` + + :return: Dictionary with relationship_id, attribute_id, and schema names. + :rtype: ``dict[str, Any]`` + + :raises HttpError: If the Web API request fails. + """ + url = f"{self.api}/RelationshipDefinitions" + + # Build the payload by combining relationship and lookup metadata + payload = relationship.to_dict() + payload["Lookup"] = lookup.to_dict() + + headers = (await self._headers()).copy() + if solution: + headers["MSCRM.SolutionUniqueName"] = solution + + r = await self._request("post", url, headers=headers, json=payload) + + # Extract IDs from response headers + relationship_id = self._extract_id_from_header(r.headers.get("OData-EntityId")) + + return { + "relationship_id": relationship_id, + "relationship_schema_name": relationship.schema_name, + "lookup_schema_name": lookup.schema_name, + "referenced_entity": relationship.referenced_entity, + "referencing_entity": relationship.referencing_entity, + } + + async def _create_many_to_many_relationship( + self, + relationship, + solution: Optional[str] = None, + ) -> Dict[str, Any]: + """ + Create a many-to-many relationship. + + Posts to /RelationshipDefinitions with ManyToManyRelationshipMetadata. + + :param relationship: Relationship metadata (ManyToManyRelationshipMetadata instance). + :type relationship: ~PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata + :param solution: Optional solution unique name to add the relationship to. + :type solution: ``str`` | ``None`` + + :return: Dictionary with relationship_id and schema name. + :rtype: ``dict[str, Any]`` + + :raises HttpError: If the Web API request fails. + """ + url = f"{self.api}/RelationshipDefinitions" + + payload = relationship.to_dict() + + headers = (await self._headers()).copy() + if solution: + headers["MSCRM.SolutionUniqueName"] = solution + + r = await self._request("post", url, headers=headers, json=payload) + + # Extract ID from response header + relationship_id = self._extract_id_from_header(r.headers.get("OData-EntityId")) + + return { + "relationship_id": relationship_id, + "relationship_schema_name": relationship.schema_name, + "entity1_logical_name": relationship.entity1_logical_name, + "entity2_logical_name": relationship.entity2_logical_name, + } + + async def _delete_relationship(self, relationship_id: str) -> None: + """ + Delete a relationship by its metadata ID. + + :param relationship_id: The GUID of the relationship metadata. + :type relationship_id: ``str`` + + :raises HttpError: If the Web API request fails. + """ + url = f"{self.api}/RelationshipDefinitions({relationship_id})" + headers = (await self._headers()).copy() + headers["If-Match"] = "*" + await self._request("delete", url, headers=headers) + + async def _get_relationship(self, schema_name: str) -> Optional[Dict[str, Any]]: + """ + Retrieve relationship metadata by schema name. + + :param schema_name: The schema name of the relationship. + :type schema_name: ``str`` + + :return: Relationship metadata dictionary, or None if not found. + :rtype: ``dict[str, Any]`` | ``None`` + + :raises HttpError: If the Web API request fails. + """ + url = f"{self.api}/RelationshipDefinitions" + params = {"$filter": f"SchemaName eq '{self._escape_odata_quotes(schema_name)}'"} + r = await self._request("get", url, headers=await self._headers(), params=params) + data = r.json() + results = data.get("value", []) + return results[0] if results else None + + async def _list_relationships( + self, + *, + filter: Optional[str] = None, + select: Optional[List[str]] = None, + ) -> List[Dict[str, Any]]: + """List all relationship definitions. + + Issues ``GET /RelationshipDefinitions`` with optional ``$filter`` and + ``$select`` query parameters. + + :param filter: Optional OData ``$filter`` expression. For example, + ``"RelationshipType eq Microsoft.Dynamics.CRM.RelationshipType'OneToManyRelationship'"`` + returns only one-to-many relationships. + :type filter: ``str`` or ``None`` + :param select: Optional list of property names to project via + ``$select``. Values are passed as-is (PascalCase). + :type select: ``list[str]`` or ``None`` + + :return: List of raw relationship metadata dictionaries (may be empty). + :rtype: ``list[dict[str, Any]]`` + + :raises HttpError: If the Web API request fails. + """ + url = f"{self.api}/RelationshipDefinitions" + params: Dict[str, str] = {} + if filter: + params["$filter"] = filter + if select: + params["$select"] = ",".join(select) + r = await self._request("get", url, headers=await self._headers(), params=params) + return (r.json()).get("value", []) + + async def _list_table_relationships( + self, + table_schema_name: str, + *, + filter: Optional[str] = None, + select: Optional[List[str]] = None, + ) -> List[Dict[str, Any]]: + """List all relationships for a specific table. + + Issues ``GET EntityDefinitions({MetadataId})/OneToManyRelationships``, + ``GET EntityDefinitions({MetadataId})/ManyToOneRelationships``, and + ``GET EntityDefinitions({MetadataId})/ManyToManyRelationships``, + then combines the results. + + :param table_schema_name: Schema name of the table (e.g. ``"account"``). + :type table_schema_name: ``str`` + :param filter: Optional OData ``$filter`` expression applied to each + sub-request. + :type filter: ``str`` or ``None`` + :param select: Optional list of property names to project via + ``$select``. Values are passed as-is (PascalCase). + :type select: ``list[str]`` or ``None`` + + :return: Combined list of one-to-many, many-to-one, and many-to-many + relationship metadata dictionaries (may be empty). + :rtype: ``list[dict[str, Any]]`` + + :raises MetadataError: If the table is not found. + :raises HttpError: If the Web API request fails. + """ + ent = await self._get_entity_by_table_schema_name(table_schema_name) + if not ent or not ent.get("MetadataId"): + raise MetadataError( + f"Table '{table_schema_name}' not found.", + subcode=METADATA_TABLE_NOT_FOUND, + ) + + metadata_id = ent["MetadataId"] + # OneToMany/ManyToOne share the same property surface (ReferencedEntity, + # ReferencingEntity, etc.). ManyToManyRelationshipMetadata has a + # different schema -- it only exposes SchemaName plus Entity1/Entity2 + # fields, not ReferencedEntity or ReferencingEntity. Sending a $select + # that includes those properties to the ManyToMany endpoint causes a + # 400: "Could not find a property named 'ReferencedEntity' on type + # 'ManyToManyRelationshipMetadata'". Use separate param dicts. + one_to_many_params: Dict[str, str] = {} + many_to_many_params: Dict[str, str] = {} + if filter: + one_to_many_params["$filter"] = filter + many_to_many_params["$filter"] = filter + if select: + one_to_many_params["$select"] = ",".join(select) + + one_to_many_url = f"{self.api}/EntityDefinitions({metadata_id})/OneToManyRelationships" + many_to_one_url = f"{self.api}/EntityDefinitions({metadata_id})/ManyToOneRelationships" + many_to_many_url = f"{self.api}/EntityDefinitions({metadata_id})/ManyToManyRelationships" + + headers = await self._headers() + r1, r2, r3 = await asyncio.gather( + self._request("get", one_to_many_url, headers=headers, params=one_to_many_params), + self._request("get", many_to_one_url, headers=headers, params=one_to_many_params), + self._request("get", many_to_many_url, headers=headers, params=many_to_many_params), + ) + + return r1.json().get("value", []) + r2.json().get("value", []) + r3.json().get("value", []) + + def _extract_id_from_header(self, header_value: Optional[str]) -> Optional[str]: + """ + Extract a GUID from an OData-EntityId header value. + + :param header_value: The header value containing a URL with GUID. + :type header_value: ``str`` | ``None`` + + :return: Extracted GUID or None if not found. + :rtype: ``str`` | ``None`` + """ + if not header_value: + return None + match = re.search(r"\(([0-9a-fA-F-]+)\)", header_value) + return match.group(1) if match else None diff --git a/src/PowerPlatform/Dataverse/aio/data/_async_upload.py b/src/PowerPlatform/Dataverse/aio/data/_async_upload.py new file mode 100644 index 00000000..59c36967 --- /dev/null +++ b/src/PowerPlatform/Dataverse/aio/data/_async_upload.py @@ -0,0 +1,193 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +"""Async file upload helpers.""" + +from __future__ import annotations + +import asyncio +import math +from pathlib import Path +from typing import Optional +from urllib.parse import quote + + +class _AsyncFileUploadMixin: + """Async file upload capabilities (small + chunk) with auto selection.""" + + async def _upload_file( + self, + table_schema_name: str, + record_id: str, + file_name_attribute: str, + path: str, + mode: Optional[str] = None, + mime_type: Optional[str] = None, + if_none_match: bool = True, + ) -> None: + """Upload a file to a Dataverse file column with automatic method selection. + + Parameters + ---------- + table_schema_name : :class:`str` + Table schema name, e.g. "account" or "new_MyTestTable". + record_id : :class:`str` + GUID of the target record. + file_name_attribute : :class:`str` + Schema name of the file column attribute (e.g., "new_Document"). If the column doesn't exist, it will be created. + path : :class:`str` + Local filesystem path to the file. + mode : :class:`str` | None + Upload strategy: "auto" (default), "small", or "chunk". + mime_type : :class:`str` | None + Explicit MIME type. If omitted falls back to application/octet-stream. + if_none_match : :class:`bool` + When True (default) only succeeds if column empty. When False overwrites (If-Match: *). + """ + # Resolve entity set from table schema name + entity_set = await self._entity_set_from_schema_name(table_schema_name) + + # Check if the file column exists, create it if it doesn't + entity_metadata = await self._get_entity_by_table_schema_name(table_schema_name) + if entity_metadata: + metadata_id = entity_metadata.get("MetadataId") + if metadata_id: + attr_metadata = await self._get_attribute_metadata(metadata_id, file_name_attribute) + if not attr_metadata: + # Attribute doesn't exist, create it + await self._create_columns(table_schema_name, {file_name_attribute: "file"}) + # Wait for the attribute to become visible in the data API + # Raises RuntimeError with underlying exception if timeout occurs + await self._wait_for_attribute_visibility(entity_set, file_name_attribute) + + mode = (mode or "auto").lower() + + if mode == "auto": + p = Path(path) + if not p.is_file(): + raise FileNotFoundError(f"File not found: {path}") + size = p.stat().st_size + mode = "small" if size < 128 * 1024 * 1024 else "chunk" + + # Convert schema name to lowercase logical name for URL usage + logical_name = file_name_attribute.lower() + + if mode == "small": + return await self._upload_file_small( + entity_set, record_id, logical_name, path, content_type=mime_type, if_none_match=if_none_match + ) + if mode == "chunk": + return await self._upload_file_chunk(entity_set, record_id, logical_name, path, if_none_match=if_none_match) + raise ValueError(f"Invalid mode '{mode}'. Use 'auto', 'small', or 'chunk'.") + + async def _upload_file_small( + self, + entity_set: str, + record_id: str, + file_name_attribute: str, + path: str, + content_type: Optional[str] = None, + if_none_match: bool = True, + ) -> None: + """Upload a file (<128MB) via single PATCH.""" + if not record_id: + raise ValueError("record_id required") + p = Path(path) + if not p.is_file(): + raise FileNotFoundError(f"File not found: {path}") + size = p.stat().st_size + limit = 128 * 1024 * 1024 + if size > limit: + raise ValueError(f"File size {size} exceeds single-upload limit {limit}; use chunk mode.") + data = await asyncio.to_thread(p.read_bytes) + fname = p.name + key = self._format_key(record_id) + url = f"{self.api}/{entity_set}{key}/{file_name_attribute}" + headers = { + "Content-Type": content_type or "application/octet-stream", + "x-ms-file-name": fname, + } + if if_none_match: + headers["If-None-Match"] = "null" + else: + headers["If-Match"] = "*" + # Single PATCH upload; allow default success codes (includes 204) + await self._request("patch", url, headers=headers, data=data) + + async def _upload_file_chunk( + self, + entity_set: str, + record_id: str, + file_name_attribute: str, + path: str, + if_none_match: bool = True, + ) -> None: + """Stream a local file using Dataverse native chunked PATCH protocol. + 1. Initial PATCH with header x-ms-transfer-mode: chunked (empty body) to start session. + 2. Subsequent PATCH calls to Location URL including sessiontoken with binary body segments and headers. Returns 206 for partial chunks and 204 on final. + + Parameters + ---------- + entity_set : :class:`str` + Target entity set (plural logical name), e.g. "accounts". + record_id : :class:`str` + GUID of the target record. + file_name_attribute : :class:`str` + Logical name of the file column attribute. + path : :class:`str` + Local filesystem path to the file. + if_none_match : :class:`bool` + When True sends ``If-None-Match: null`` to only succeed if the column is currently empty. + Set False to always overwrite (uses ``If-Match: *``). + + Returns + ------- + None + Returns nothing on success. Any failure raises an exception. + """ + if not record_id: + raise ValueError("record_id required") + p = Path(path) + if not p.is_file(): + raise FileNotFoundError(f"File not found: {path}") + total_size = p.stat().st_size + fname = p.name + key = self._format_key(record_id) + init_url = f"{self.api}/{entity_set}{key}/{file_name_attribute}?x-ms-file-name={quote(fname)}" + headers = { + "x-ms-transfer-mode": "chunked", + } + if if_none_match: + headers["If-None-Match"] = "null" + else: + headers["If-Match"] = "*" + r_init = await self._request("patch", init_url, headers=headers, data=b"") + location = r_init.headers.get("Location") or r_init.headers.get("location") + if not location: + raise RuntimeError("Missing Location header with sessiontoken for chunked upload") + rec_hdr = r_init.headers.get("x-ms-chunk-size") or r_init.headers.get("X-MS-CHUNK-SIZE") + try: + recommended_size = int(rec_hdr) if rec_hdr else None + except Exception: # noqa: BLE001 + recommended_size = None + effective_size = recommended_size or (4 * 1024 * 1024) + if effective_size <= 0: + raise ValueError("effective chunk size must be positive") + total_chunks = int(math.ceil(total_size / effective_size)) if total_size else 1 + uploaded_bytes = 0 + with p.open("rb") as fh: + for _ in range(total_chunks): + chunk = await asyncio.to_thread(fh.read, effective_size) + if not chunk: + break + start = uploaded_bytes + end = start + len(chunk) - 1 + c_headers = { + "x-ms-file-name": fname, + "Content-Type": "application/octet-stream", + "Content-Range": f"bytes {start}-{end}/{total_size}", + "Content-Length": str(len(chunk)), + } + # Each chunk returns 206 (partial) or 204 (final). Accept both. + await self._request("patch", location, headers=c_headers, data=chunk, expected=(206, 204)) + uploaded_bytes += len(chunk) diff --git a/src/PowerPlatform/Dataverse/aio/models/__init__.py b/src/PowerPlatform/Dataverse/aio/models/__init__.py new file mode 100644 index 00000000..f4c2e3d0 --- /dev/null +++ b/src/PowerPlatform/Dataverse/aio/models/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +""" +Async data models and type definitions for the Dataverse SDK. + +Provides async-specific models for Dataverse entities: + +- :class:`~PowerPlatform.Dataverse.aio.models.async_query_builder.AsyncQueryBuilder`: Async fluent query builder. +- :class:`~PowerPlatform.Dataverse.aio.models.async_fetchxml_query.AsyncFetchXmlQuery`: Async FetchXML query. +""" + +__all__ = [] diff --git a/src/PowerPlatform/Dataverse/aio/models/async_fetchxml_query.py b/src/PowerPlatform/Dataverse/aio/models/async_fetchxml_query.py new file mode 100644 index 00000000..c10ca6f6 --- /dev/null +++ b/src/PowerPlatform/Dataverse/aio/models/async_fetchxml_query.py @@ -0,0 +1,158 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +"""AsyncFetchXmlQuery — inert async query object returned by AsyncQueryOperations.fetchxml().""" + +from __future__ import annotations + +import warnings +import xml.etree.ElementTree as _ET +from typing import AsyncIterator, List, TYPE_CHECKING +from urllib.parse import unquote as _url_unquote, quote as _url_quote + +from ...core.errors import ValidationError +from ...models.fetchxml_query import _MAX_URL_LENGTH, _MAX_PAGES, _PREFER_HEADER +from ...models.record import QueryResult, Record + +if TYPE_CHECKING: + from ..async_client import AsyncDataverseClient + + +__all__ = ["AsyncFetchXmlQuery"] + + +class AsyncFetchXmlQuery: + """Inert async FetchXML query object. No HTTP request is made until + :meth:`execute` or :meth:`execute_pages` is called. + + Obtained via ``client.query.fetchxml(xml)``. + + :param xml: Stripped, well-formed FetchXML string. + :param entity_name: Entity schema name from the ```` element. + :param client: Parent :class:`~PowerPlatform.Dataverse.aio.async_client.AsyncDataverseClient`. + """ + + def __init__(self, xml: str, entity_name: str, client: "AsyncDataverseClient") -> None: + self._xml = xml + self._entity_name = entity_name + self._client = client + + async def execute(self) -> QueryResult: + """Execute the FetchXML query and return all results as a :class:`QueryResult`. + + Awaitable — fetches all pages and holds every record in memory before + returning. Use :meth:`execute_pages` when the result set may be large. + + :return: All matching records across all pages. + :rtype: :class:`~PowerPlatform.Dataverse.models.record.QueryResult` + + Example:: + + rows = await client.query.fetchxml(xml).execute() + df = rows.to_dataframe() + """ + all_records: List[Record] = [] + async for page in self.execute_pages(): + all_records.extend(page.records) + return QueryResult(all_records) + + async def execute_pages(self) -> AsyncIterator[QueryResult]: + """Lazily yield one :class:`QueryResult` per HTTP page. + + Each iteration fires one HTTP request and yields one page. One-shot — + do not iterate more than once. + + :return: Async iterator of per-page :class:`QueryResult` objects. + :rtype: AsyncIterator[:class:`~PowerPlatform.Dataverse.models.record.QueryResult`] + + Example:: + + async for page in client.query.fetchxml(xml).execute_pages(): + process(page.to_dataframe()) + """ + current_xml = self._xml + page_num = 1 + page_count = 0 + + async with self._client._scoped_odata() as od: + entity_set = await od._entity_set_from_schema_name(self._entity_name) + base_url = f"{od.api}/{entity_set}" + + while True: + page_count += 1 + if page_count > _MAX_PAGES: + raise ValidationError( + f"FetchXML paging exceeded {_MAX_PAGES} pages. " + "This may indicate a runaway query or a bug in paging cookie propagation." + ) + + encoded_len = len(base_url) + len("?fetchXml=") + len(_url_quote(current_xml, safe="")) + if encoded_len > _MAX_URL_LENGTH: + raise ValidationError( + f"FetchXML request URL exceeds {_MAX_URL_LENGTH} characters after encoding. " + "Simplify the query or reduce attributes/conditions." + ) + + r = await od._request( + "get", + base_url, + headers={"Prefer": _PREFER_HEADER}, + params={"fetchXml": current_xml}, + ) + try: + data = r.json() + except Exception: + data = {} + + items = data.get("value") if isinstance(data, dict) else None + page_records: List[Record] = [] + if isinstance(items, list): + for item in items: + if isinstance(item, dict): + page_records.append(Record.from_api_response(self._entity_name, item)) + + yield QueryResult(page_records) + + more_raw = data.get("@Microsoft.Dynamics.CRM.morerecords", False) if isinstance(data, dict) else False + more = more_raw is True or (isinstance(more_raw, str) and more_raw.lower() == "true") + if not more: + break + + raw_cookie = ( + data.get("@Microsoft.Dynamics.CRM.fetchxmlpagingcookie", "") if isinstance(data, dict) else "" + ) + + _cookie_parse_error = False + if raw_cookie: + try: + cookie_el = _ET.fromstring(raw_cookie) + inner_encoded = cookie_el.get("pagingcookie", "") + if inner_encoded: + cookie = _url_unquote(_url_unquote(inner_encoded)) + page_num = int(cookie_el.get("pagenumber", str(page_num + 1))) + fetch_el = _ET.fromstring(current_xml) + fetch_el.set("paging-cookie", cookie) + fetch_el.set("page", str(page_num)) + current_xml = _ET.tostring(fetch_el, encoding="unicode") + continue + except (_ET.ParseError, ValueError) as exc: + warnings.warn( + f"FetchXML paging cookie could not be parsed ({exc}); " "falling back to simple paging.", + UserWarning, + stacklevel=2, + ) + _cookie_parse_error = True + + if not _cookie_parse_error: + warnings.warn( + "Dataverse did not return a paging cookie; falling back to simple paging " + "(page-number increment only). Simple paging is capped at 50,000 records " + "and degrades in performance at high page numbers. Consider reordering on " + "a root-entity column to enable cookie-based paging.", + UserWarning, + stacklevel=2, + ) + page_num += 1 + fetch_el = _ET.fromstring(current_xml) + fetch_el.set("page", str(page_num)) + current_xml = _ET.tostring(fetch_el, encoding="unicode") diff --git a/src/PowerPlatform/Dataverse/aio/models/async_query_builder.py b/src/PowerPlatform/Dataverse/aio/models/async_query_builder.py new file mode 100644 index 00000000..e48b3198 --- /dev/null +++ b/src/PowerPlatform/Dataverse/aio/models/async_query_builder.py @@ -0,0 +1,141 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +"""AsyncQueryBuilder — async execution layer over the shared QueryBuilder.""" + +from __future__ import annotations + +from typing import AsyncIterator, List + +from ...models.query_builder import _QueryBuilderBase +from ...models.record import QueryResult, Record + +__all__ = ["AsyncQueryBuilder"] + + +class AsyncQueryBuilder(_QueryBuilderBase): + """Async-capable QueryBuilder. + + Identical fluent interface to :class:`~PowerPlatform.Dataverse.models.query_builder.QueryBuilder` + — all chaining methods (``select``, ``where``, ``order_by``, ``top``, ``page_size``, + ``count``, ``expand``, ``include_annotations``, ``include_formatted_values``) are + inherited unchanged. Only the execution methods are overridden as coroutines. + + Obtained via ``client.query.builder(table)`` on an async client. + + Example:: + + from PowerPlatform.Dataverse.models.filters import col + + result = await (client.query.builder("account") + .select("name", "revenue") + .where(col("statecode") == 0) + .order_by("revenue", descending=True) + .top(100) + .execute()) + for record in result: + print(record["name"]) + """ + + async def execute(self) -> QueryResult: + """Execute the query and return all results as a :class:`QueryResult`. + + Awaitable — fetches all pages and holds every record in memory before + returning. Use :meth:`execute_pages` for lazy per-page streaming. + + At least one of ``select()``, ``where()``, ``top()``, or + ``page_size()`` must be called first to prevent accidental full-table + scans. + + :return: All matching records across all pages. + :rtype: :class:`~PowerPlatform.Dataverse.models.record.QueryResult` + :raises ValueError: If no scope constraint has been set. + :raises RuntimeError: If the builder was not created via + ``client.query.builder()``. + + Example:: + + result = await (client.query.builder("account") + .select("name") + .where(col("statecode") == 0) + .execute()) + for record in result: + print(record["name"]) + """ + if self._query_ops is None: + raise RuntimeError( + "Cannot execute: query was not created via client.query.builder(). " + "Use build() and pass parameters to client.records.list() instead." + ) + if not self._select and not self._filter_parts and self._top is None and self._page_size is None: + raise ValueError( + "At least one of select(), where(), top(), or page_size() must be called before " + "execute() to prevent accidental full-table scans." + ) + params = self.build() + client = self._query_ops._client + all_records: List[Record] = [] + async with client._scoped_odata() as od: + async for page in od._get_multiple( + params["table"], + select=params.get("select"), + filter=params.get("filter"), + orderby=params.get("orderby"), + top=params.get("top"), + expand=params.get("expand"), + page_size=params.get("page_size"), + count=params.get("count", False), + include_annotations=params.get("include_annotations"), + ): + all_records.extend(Record.from_api_response(params["table"], row) for row in page) + return QueryResult(all_records) + + async def execute_pages(self) -> AsyncIterator[QueryResult]: + """Lazily yield one :class:`QueryResult` per HTTP page. + + Each iteration triggers one network request. One-shot — do not + iterate more than once. + + At least one of ``select()``, ``where()``, ``top()``, or + ``page_size()`` must be called first to prevent accidental full-table + scans. + + :return: Async iterator of per-page + :class:`~PowerPlatform.Dataverse.models.record.QueryResult` objects. + :rtype: AsyncIterator[:class:`~PowerPlatform.Dataverse.models.record.QueryResult`] + :raises ValueError: If no scope constraint has been set. + :raises RuntimeError: If the builder was not created via + ``client.query.builder()``. + + Example:: + + async for page in (client.query.builder("account") + .select("name") + .execute_pages()): + process(page.to_dataframe()) + """ + if self._query_ops is None: + raise RuntimeError( + "Cannot execute: query was not created via client.query.builder(). " + "Use build() and pass parameters to client.records.list() instead." + ) + if not self._select and not self._filter_parts and self._top is None and self._page_size is None: + raise ValueError( + "At least one of select(), where(), top(), or page_size() must be called before " + "execute_pages() to prevent accidental full-table scans." + ) + params = self.build() + client = self._query_ops._client + async with client._scoped_odata() as od: + async for page in od._get_multiple( + params["table"], + select=params.get("select"), + filter=params.get("filter"), + orderby=params.get("orderby"), + top=params.get("top"), + expand=params.get("expand"), + page_size=params.get("page_size"), + count=params.get("count", False), + include_annotations=params.get("include_annotations"), + ): + yield QueryResult([Record.from_api_response(params["table"], row) for row in page]) diff --git a/src/PowerPlatform/Dataverse/aio/operations/__init__.py b/src/PowerPlatform/Dataverse/aio/operations/__init__.py new file mode 100644 index 00000000..62bb4281 --- /dev/null +++ b/src/PowerPlatform/Dataverse/aio/operations/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +""" +Async operation namespaces for the Dataverse SDK. + +This module contains the async operation namespace classes that organize +SDK operations into logical groups: records, query, tables, files, and batch. +""" + +from typing import List + +__all__: List[str] = [] diff --git a/src/PowerPlatform/Dataverse/aio/operations/async_batch.py b/src/PowerPlatform/Dataverse/aio/operations/async_batch.py new file mode 100644 index 00000000..3b95fea3 --- /dev/null +++ b/src/PowerPlatform/Dataverse/aio/operations/async_batch.py @@ -0,0 +1,174 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +"""Async batch operation namespaces for the Dataverse SDK.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, List + +from ...data._batch_base import _ChangeSet +from ...operations.batch import ( + BatchDataFrameOperations, + BatchQueryOperations, + BatchRecordOperations, + BatchTableOperations, + ChangeSetRecordOperations, +) +from ..data._async_batch import _AsyncBatchClient +from ...models.batch import BatchResult + +if TYPE_CHECKING: + from ..async_client import AsyncDataverseClient + +__all__ = [ + "AsyncBatchRequest", + "AsyncBatchOperations", + "AsyncChangeSet", +] + + +# --------------------------------------------------------------------------- +# Changeset +# --------------------------------------------------------------------------- + + +class AsyncChangeSet: + """ + A transactional group of single-record write operations. + + All operations succeed or are rolled back together. Use as an async context + manager or call :attr:`records` to add operations directly. + + Do not instantiate directly; use :meth:`AsyncBatchRequest.changeset`. + + Example:: + + async with batch.changeset() as cs: + ref = cs.records.create("contact", {"firstname": "Alice"}) + cs.records.update("account", account_id, { + "primarycontactid@odata.bind": ref + }) + """ + + def __init__(self, internal: _ChangeSet) -> None: + self._internal = internal + self.records = ChangeSetRecordOperations(internal) + + async def __aenter__(self) -> "AsyncChangeSet": + return self + + async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: + return None + + +# --------------------------------------------------------------------------- +# AsyncBatchRequest and AsyncBatchOperations +# --------------------------------------------------------------------------- + + +class AsyncBatchRequest: + """ + Builder for constructing and executing a Dataverse OData ``$batch`` request. + + Obtain via :meth:`AsyncBatchOperations.new` (``client.batch.new()``). Add operations + through :attr:`records`, :attr:`tables`, :attr:`query`, and :attr:`dataframe`, + optionally group writes into a :meth:`changeset`, then call :meth:`execute`. + + Operations are executed sequentially in the order added. The resulting + :class:`~PowerPlatform.Dataverse.models.batch.BatchResult` contains one + :class:`~PowerPlatform.Dataverse.models.batch.BatchItemResponse` per HTTP + request dispatched (some operations expand to multiple requests). + + .. note:: + Maximum 1000 HTTP operations per batch. + + Example:: + + batch = client.batch.new() + batch.records.create("account", {"name": "Contoso"}) + batch.tables.get("account") + async with batch.changeset() as cs: + ref = cs.records.create("contact", {"firstname": "Alice"}) + cs.records.update("account", account_id, { + "primarycontactid@odata.bind": ref + }) + result = await batch.execute() + """ + + def __init__(self, client: "AsyncDataverseClient") -> None: + self._client = client + self._items: List[Any] = [] + self._content_id_counter: List[int] = [1] # shared across all changesets + self.records = BatchRecordOperations(self) + self.tables = BatchTableOperations(self) + self.query = BatchQueryOperations(self) + self.dataframe = BatchDataFrameOperations(self) + + def changeset(self) -> AsyncChangeSet: + """ + Create a new :class:`AsyncChangeSet` attached to this batch. + + The changeset is added to the batch immediately. Operations added to + the returned :class:`AsyncChangeSet` via ``cs.records.*`` execute atomically. + + :returns: A new :class:`AsyncChangeSet` ready to receive operations. + + Example:: + + async with batch.changeset() as cs: + cs.records.create("account", {"name": "ACME"}) + cs.records.create("contact", {"firstname": "Bob"}) + """ + internal = _ChangeSet(_counter=self._content_id_counter) + self._items.append(internal) + return AsyncChangeSet(internal) + + async def execute(self, *, continue_on_error: bool = False) -> BatchResult: + """ + Submit the batch to Dataverse and return all responses. + + :param continue_on_error: When False (default), Dataverse stops at the + first failure and returns that operation's error as a 4xx response. + When True, ``Prefer: odata.continue-on-error`` is sent and all + operations are attempted. + :returns: :class:`~PowerPlatform.Dataverse.models.batch.BatchResult` + with one entry per HTTP operation in submission order. + :raises ValidationError: If the batch exceeds 1000 operations or an + unsupported column type is specified. + :raises MetadataError: If metadata pre-resolution fails (table or + column not found) for ``tables.delete``, ``tables.add_columns``, + or ``tables.remove_columns``. + :raises HttpError: On HTTP-level failures (auth, server error, etc.) + that prevent the batch from executing. + """ + async with self._client._scoped_odata() as od: + return await _AsyncBatchClient(od).execute(self._items, continue_on_error=continue_on_error) + + +class AsyncBatchOperations: + """ + Async namespace for batch operations (``client.batch``). + + Accessed via ``client.batch``. Use :meth:`new` to create an + :class:`AsyncBatchRequest` builder. + + :param client: The parent :class:`~PowerPlatform.Dataverse.aio.async_client.AsyncDataverseClient` instance. + + Example:: + + batch = client.batch.new() + batch.records.create("account", {"name": "Fabrikam"}) + result = await batch.execute() + """ + + def __init__(self, client: "AsyncDataverseClient") -> None: + self._client = client + + def new(self) -> AsyncBatchRequest: + """ + Create a new empty :class:`AsyncBatchRequest` builder. + + :returns: An empty :class:`AsyncBatchRequest`. + """ + return AsyncBatchRequest(self._client) diff --git a/src/PowerPlatform/Dataverse/aio/operations/async_dataframe.py b/src/PowerPlatform/Dataverse/aio/operations/async_dataframe.py new file mode 100644 index 00000000..fba7b334 --- /dev/null +++ b/src/PowerPlatform/Dataverse/aio/operations/async_dataframe.py @@ -0,0 +1,309 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +"""Async DataFrame CRUD operations namespace for the Dataverse SDK.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Dict, List, Optional + +import pandas as pd + +from ...utils._pandas import dataframe_to_records + +if TYPE_CHECKING: + from ..async_client import AsyncDataverseClient + + +__all__ = ["AsyncDataFrameOperations"] + + +class AsyncDataFrameOperations: + """Async namespace for pandas DataFrame CRUD operations. + + Accessed via ``client.dataframe``. Provides DataFrame-oriented wrappers + around the async record-level CRUD operations. + + :param client: The parent :class:`~PowerPlatform.Dataverse.aio.async_client.AsyncDataverseClient` instance. + :type client: ~PowerPlatform.Dataverse.aio.async_client.AsyncDataverseClient + + Example:: + + import pandas as pd + + async with AsyncDataverseClient(base_url, credential) as client: + + # Query records as a DataFrame via SQL + df = await client.dataframe.sql( + "SELECT TOP 100 name FROM account WHERE statecode = 0" + ) + + # Create records from a DataFrame + new_df = pd.DataFrame([{"name": "Contoso"}, {"name": "Fabrikam"}]) + new_df["accountid"] = await client.dataframe.create("account", new_df) + + # Update records + new_df["telephone1"] = ["555-0100", "555-0200"] + await client.dataframe.update("account", new_df, id_column="accountid") + + # Delete records + await client.dataframe.delete("account", new_df["accountid"]) + """ + + def __init__(self, client: "AsyncDataverseClient") -> None: + self._client = client + + # --------------------------------------------------------------------- sql + + async def sql(self, sql: str) -> pd.DataFrame: + """Execute a SQL query and return the results as a pandas DataFrame. + + Delegates to :meth:`~PowerPlatform.Dataverse.aio.operations.async_query.AsyncQueryOperations.sql` + and converts the list of records into a single DataFrame. + + :param sql: Supported SQL SELECT statement. + :type sql: :class:`str` + + :return: DataFrame containing all result rows. Returns an empty + DataFrame when no rows match. + :rtype: ~pandas.DataFrame + + :raises ~PowerPlatform.Dataverse.core.errors.ValidationError: + If ``sql`` is not a string or is empty. + + Example: + SQL query to DataFrame:: + + df = await client.dataframe.sql( + "SELECT TOP 100 name, revenue FROM account " + "WHERE statecode = 0 ORDER BY revenue" + ) + print(f"Got {len(df)} rows") + print(df.head()) + + Aggregate query to DataFrame:: + + df = await client.dataframe.sql( + "SELECT a.name, COUNT(c.contactid) as cnt " + "FROM account a " + "JOIN contact c ON a.accountid = c.parentcustomerid " + "GROUP BY a.name" + ) + """ + rows = await self._client.query.sql(sql) + if not rows: + return pd.DataFrame() + return pd.DataFrame.from_records([r.data for r in rows]) + + # ----------------------------------------------------------------- create + + async def create( + self, + table: str, + records: pd.DataFrame, + ) -> pd.Series: + """Create records from a pandas DataFrame. + + :param table: Schema name of the table (e.g. ``"account"`` or ``"new_MyTestTable"``). + :type table: :class:`str` + :param records: DataFrame where each row is a record to create. + :type records: ~pandas.DataFrame + + :return: Series of created record GUIDs, aligned with the input DataFrame index. + :rtype: ~pandas.Series + + :raises TypeError: If ``records`` is not a pandas DataFrame. + :raises ValueError: If ``records`` is empty or the number of returned + IDs does not match the number of input rows. + + .. tip:: + All rows are sent in a single ``CreateMultiple`` request. For very + large DataFrames, consider splitting into smaller batches to avoid + request timeouts. + + Example: + Create records from a DataFrame:: + + import pandas as pd + + df = pd.DataFrame([ + {"name": "Contoso", "telephone1": "555-0100"}, + {"name": "Fabrikam", "telephone1": "555-0200"}, + ]) + df["accountid"] = await client.dataframe.create("account", df) + """ + if not isinstance(records, pd.DataFrame): + raise TypeError("records must be a pandas DataFrame") + + if records.empty: + raise ValueError("records must be a non-empty DataFrame") + + record_list = dataframe_to_records(records) + + # Detect rows where all values were NaN/None (empty dicts after normalization) + empty_rows = [records.index[i] for i, r in enumerate(record_list) if not r] + if empty_rows: + raise ValueError( + f"Records at index(es) {empty_rows} have no non-null values. " + "All rows must contain at least one field to create." + ) + + ids = await self._client.records.create(table, record_list) + + if len(ids) != len(records): + raise ValueError(f"Server returned {len(ids)} IDs for {len(records)} input rows") + + return pd.Series(ids, index=records.index) + + # ----------------------------------------------------------------- update + + async def update( + self, + table: str, + changes: pd.DataFrame, + id_column: str, + clear_nulls: bool = False, + ) -> None: + """Update records from a pandas DataFrame. + + Each row in the DataFrame represents an update. The ``id_column`` specifies which + column contains the record GUIDs. + + :param table: Schema name of the table (e.g. ``"account"`` or ``"new_MyTestTable"``). + :type table: :class:`str` + :param changes: DataFrame where each row contains a record GUID and the fields to update. + :type changes: ~pandas.DataFrame + :param id_column: Name of the DataFrame column containing record GUIDs. + :type id_column: :class:`str` + :param clear_nulls: When ``False`` (default), missing values (NaN/None) are skipped + (the field is left unchanged on the server). When ``True``, missing values are sent + as ``null`` to Dataverse, clearing the field. Use ``True`` only when you intentionally + want NaN/None values to clear fields. + :type clear_nulls: :class:`bool` + + :raises TypeError: If ``changes`` is not a pandas DataFrame. + :raises ValueError: If ``changes`` is empty, ``id_column`` is not found in the + DataFrame, ``id_column`` contains invalid (non-string, empty, or whitespace-only) + values, or no updatable columns exist besides ``id_column``. + When ``clear_nulls`` is ``False`` (default), rows where all change values + are NaN/None produce empty patches and are silently skipped. If all rows + are skipped, the method returns without making an API call. When + ``clear_nulls`` is ``True``, NaN/None values become explicit nulls, so + rows are never skipped. + + .. tip:: + All rows are sent in a single ``UpdateMultiple`` request (or a + single PATCH for one row). For very large DataFrames, consider + splitting into smaller batches to avoid request timeouts. + + Example: + Update records with different values per row:: + + import pandas as pd + + df = pd.DataFrame([ + {"accountid": "guid-1", "telephone1": "555-0100"}, + {"accountid": "guid-2", "telephone1": "555-0200"}, + ]) + await client.dataframe.update("account", df, id_column="accountid") + + Broadcast the same change to all records:: + + df = pd.DataFrame({"accountid": ["guid-1", "guid-2", "guid-3"]}) + df["websiteurl"] = "https://example.com" + await client.dataframe.update("account", df, id_column="accountid") + + Clear a field by setting clear_nulls=True:: + + df = pd.DataFrame([{"accountid": "guid-1", "websiteurl": None}]) + await client.dataframe.update("account", df, id_column="accountid", clear_nulls=True) + """ + if not isinstance(changes, pd.DataFrame): + raise TypeError("changes must be a pandas DataFrame") + if changes.empty: + raise ValueError("changes must be a non-empty DataFrame") + if id_column not in changes.columns: + raise ValueError(f"id_column '{id_column}' not found in DataFrame columns") + + raw_ids = changes[id_column].tolist() + invalid = [changes.index[i] for i, v in enumerate(raw_ids) if not isinstance(v, str) or not v.strip()] + if invalid: + raise ValueError( + f"id_column '{id_column}' contains invalid values at row index(es) {invalid}. " + "All IDs must be non-empty strings." + ) + ids = [v.strip() for v in raw_ids] + + change_columns = [column for column in changes.columns if column != id_column] + if not change_columns: + raise ValueError( + "No columns to update. The DataFrame must contain at least one column besides the id_column." + ) + change_list = dataframe_to_records(changes[change_columns], na_as_null=clear_nulls) + + # Filter out rows where all change values were NaN/None (empty dicts) + paired = [(rid, patch) for rid, patch in zip(ids, change_list) if patch] + if not paired: + return + ids_filtered: List[str] = [p[0] for p in paired] + change_filtered: List[Dict[str, Any]] = [p[1] for p in paired] + + if len(ids_filtered) == 1: + await self._client.records.update(table, ids_filtered[0], change_filtered[0]) + else: + await self._client.records.update(table, ids_filtered, change_filtered) + + # ----------------------------------------------------------------- delete + + async def delete( + self, + table: str, + ids: pd.Series, + use_bulk_delete: bool = True, + ) -> Optional[str]: + """Delete records by passing a pandas Series of GUIDs. + + :param table: Schema name of the table (e.g. ``"account"`` or ``"new_MyTestTable"``). + :type table: :class:`str` + :param ids: Series of record GUIDs to delete. + :type ids: ~pandas.Series + :param use_bulk_delete: When ``True`` (default) and ``ids`` contains multiple values, + execute the BulkDelete action and return its async job identifier. + When ``False`` each record is deleted sequentially. + :type use_bulk_delete: :class:`bool` + + :raises TypeError: If ``ids`` is not a pandas Series. + :raises ValueError: If ``ids`` contains invalid (non-string, empty, or + whitespace-only) values. + + :return: BulkDelete job ID when deleting multiple records via BulkDelete; + ``None`` when deleting a single record, using sequential deletion, or + when ``ids`` is empty. + :rtype: :class:`str` or None + + Example: + Delete records using a Series:: + + import pandas as pd + + ids = pd.Series(["guid-1", "guid-2", "guid-3"]) + await client.dataframe.delete("account", ids) + """ + if not isinstance(ids, pd.Series): + raise TypeError("ids must be a pandas Series") + + raw_list = ids.tolist() + if not raw_list: + return None + + invalid = [ids.index[i] for i, v in enumerate(raw_list) if not isinstance(v, str) or not v.strip()] + if invalid: + raise ValueError( + f"ids Series contains invalid values at index(es) {invalid}. " f"All IDs must be non-empty strings." + ) + id_list = [v.strip() for v in raw_list] + + if len(id_list) == 1: + await self._client.records.delete(table, id_list[0]) + return None + return await self._client.records.delete(table, id_list, use_bulk_delete=use_bulk_delete) diff --git a/src/PowerPlatform/Dataverse/aio/operations/async_files.py b/src/PowerPlatform/Dataverse/aio/operations/async_files.py new file mode 100644 index 00000000..da755e62 --- /dev/null +++ b/src/PowerPlatform/Dataverse/aio/operations/async_files.py @@ -0,0 +1,113 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +"""Async file operations namespace for the Dataverse SDK.""" + +from __future__ import annotations + +from typing import Optional, TYPE_CHECKING + +if TYPE_CHECKING: + from ..async_client import AsyncDataverseClient + + +__all__ = ["AsyncFileOperations"] + + +class AsyncFileOperations: + """Async namespace for file operations. + + Accessed via ``client.files``. Provides file upload operations for + Dataverse file columns. + + :param client: The parent :class:`~PowerPlatform.Dataverse.aio.async_client.AsyncDataverseClient` instance. + :type client: ~PowerPlatform.Dataverse.aio.async_client.AsyncDataverseClient + + Example:: + + async with AsyncDataverseClient(base_url, credential) as client: + + await client.files.upload( + "account", account_id, "new_Document", "/path/to/file.pdf" + ) + """ + + def __init__(self, client: "AsyncDataverseClient") -> None: + self._client = client + + # ----------------------------------------------------------------- upload + + async def upload( + self, + table: str, + record_id: str, + file_column: str, + path: str, + *, + mode: Optional[str] = None, + mime_type: Optional[str] = None, + if_none_match: bool = True, + ) -> None: + """Upload a file to a Dataverse file column. + + :param table: Schema name of the table (e.g. ``"account"`` or + ``"new_MyTestTable"``). + :type table: :class:`str` + :param record_id: GUID of the target record. + :type record_id: :class:`str` + :param file_column: Schema name of the file column attribute (e.g., + ``"new_Document"``). If the column doesn't exist, it will be + created automatically. + :type file_column: :class:`str` + :param path: Local filesystem path to the file. The stored filename + will be the basename of this path. + :type path: :class:`str` + :param mode: Upload strategy: ``"auto"`` (default), ``"small"``, or + ``"chunk"``. Auto mode selects small or chunked upload based on + file size. + :type mode: :class:`str` or None + :param mime_type: Explicit MIME type to store with the file (e.g. + ``"application/pdf"``). If not provided, defaults to + ``"application/octet-stream"``. + :type mime_type: :class:`str` or None + :param if_none_match: When True (default), sends + ``If-None-Match: null`` header to only succeed if the column is + currently empty. Set False to always overwrite using + ``If-Match: *``. + :type if_none_match: :class:`bool` + + :raises ~PowerPlatform.Dataverse.core.errors.HttpError: + If the upload fails or the file column is not empty when + ``if_none_match=True``. + :raises FileNotFoundError: If the specified file path does not exist. + + Example: + Upload a PDF file:: + + await client.files.upload( + "account", + account_id, + "new_Contract", + "/path/to/contract.pdf", + mime_type="application/pdf", + ) + + Upload with auto mode selection:: + + await client.files.upload( + "email", + email_id, + "new_Attachment", + "/path/to/large_file.zip", + ) + """ + async with self._client._scoped_odata() as od: + await od._upload_file( + table, + record_id, + file_column, + path, + mode=mode, + mime_type=mime_type, + if_none_match=if_none_match, + ) diff --git a/src/PowerPlatform/Dataverse/aio/operations/async_query.py b/src/PowerPlatform/Dataverse/aio/operations/async_query.py new file mode 100644 index 00000000..7f378380 --- /dev/null +++ b/src/PowerPlatform/Dataverse/aio/operations/async_query.py @@ -0,0 +1,371 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +"""Async query operations namespace for the Dataverse SDK.""" + +from __future__ import annotations + +import xml.etree.ElementTree as _ET +from typing import Any, Dict, List, TYPE_CHECKING +from urllib.parse import quote as _url_quote + +from ...core.errors import MetadataError, ValidationError +from ..models.async_fetchxml_query import AsyncFetchXmlQuery +from ..models.async_query_builder import AsyncQueryBuilder +from ...models.fetchxml_query import _MAX_URL_LENGTH +from ...models.record import Record + +if TYPE_CHECKING: + from ..async_client import AsyncDataverseClient + + +__all__ = ["AsyncQueryOperations"] + + +class AsyncQueryOperations: + """Async namespace for query operations. + + Accessed via ``client.query``. Provides query and search operations + against Dataverse tables. + + :param client: The parent :class:`~PowerPlatform.Dataverse.aio.async_client.AsyncDataverseClient` instance. + :type client: ~PowerPlatform.Dataverse.aio.async_client.AsyncDataverseClient + + Example:: + + async with AsyncDataverseClient(base_url, credential) as client: + + # Fluent query builder (recommended) + from PowerPlatform.Dataverse.models.filters import col + + for record in await (client.query.builder("account") + .select("name", "revenue") + .where(col("statecode") == 0) + .order_by("revenue", descending=True) + .top(100) + .execute()): + print(record["name"]) + + # SQL query + rows = await client.query.sql("SELECT TOP 10 name FROM account ORDER BY name") + for row in rows: + print(row["name"]) + """ + + def __init__(self, client: "AsyncDataverseClient") -> None: + self._client = client + + # ----------------------------------------------------------------- builder + + def builder(self, table: str) -> AsyncQueryBuilder: + """Create a fluent async query builder for the specified table. + + Returns an :class:`~PowerPlatform.Dataverse.models.async_query_builder.AsyncQueryBuilder` + that can be chained with filter, select, and order methods, then + executed via ``await .execute()`` or iterated via ``async for`` with + ``.execute_pages()``. + + :param table: Table schema name (e.g. ``"account"``). + :type table: :class:`str` + :return: An AsyncQueryBuilder instance bound to this client. + :rtype: ~PowerPlatform.Dataverse.models.async_query_builder.AsyncQueryBuilder + + Example:: + + from PowerPlatform.Dataverse.models.filters import col + + result = await (client.query.builder("account") + .select("name", "revenue") + .where(col("statecode") == 0) + .order_by("revenue", descending=True) + .top(100) + .execute()) + for record in result: + print(record["name"]) + + # Lazy paged iteration + async for page in (client.query.builder("account") + .select("name") + .execute_pages()): + process(page.to_dataframe()) + """ + qb = AsyncQueryBuilder(table) + qb._query_ops = self + return qb + + # --------------------------------------------------------------- fetchxml + + def fetchxml(self, xml: str) -> AsyncFetchXmlQuery: + """Return an inert :class:`~PowerPlatform.Dataverse.models.async_fetchxml_query.AsyncFetchXmlQuery` object. + + No HTTP request is made until + :meth:`~PowerPlatform.Dataverse.models.async_fetchxml_query.AsyncFetchXmlQuery.execute` + or + :meth:`~PowerPlatform.Dataverse.models.async_fetchxml_query.AsyncFetchXmlQuery.execute_pages` + is called on the returned object. + + :param xml: Well-formed FetchXML query string. The root ```` + element determines the entity set endpoint. + :type xml: :class:`str` + :return: Inert async query object. + :rtype: :class:`~PowerPlatform.Dataverse.models.async_fetchxml_query.AsyncFetchXmlQuery` + :raises ValidationError: If the FetchXML is not a string, is empty, or exceeds the URL + length limit when encoded. + :raises ValueError: If the FetchXML is missing a root ```` element or name. + + Example:: + + query = client.query.fetchxml(\"\"\" + + + + + + \"\"\") + + # Eager — all pages collected: + result = await query.execute() + df = result.to_dataframe() + + # Lazy — one page at a time: + async for page in query.execute_pages(): + process(page.to_dataframe()) + """ + if not isinstance(xml, str): + raise ValidationError("xml must be a string") + xml = xml.strip() + if not xml: + raise ValidationError("xml must not be empty") + if len(_url_quote(xml, safe="")) > _MAX_URL_LENGTH: + raise ValidationError( + f"FetchXML exceeds the Dataverse URL length limit ({_MAX_URL_LENGTH:,} characters) when encoded. " + "Use a $batch POST request to send FetchXML in the request body where the limit is 64 KB." + ) + try: + root_el = _ET.fromstring(xml) + except _ET.ParseError as exc: + raise ValidationError(f"xml is not well-formed: {exc}") from exc + entity_el = root_el.find("entity") + if entity_el is None: + raise ValueError("FetchXML must contain an child element") + entity_name = entity_el.get("name", "") + if not entity_name: + raise ValueError("FetchXML element must have a 'name' attribute") + return AsyncFetchXmlQuery(xml, entity_name, self._client) + + # -------------------------------------------------------------------- sql + + async def sql(self, sql: str) -> List[Record]: + """Execute a read-only SQL query using the Dataverse Web API. + + The Dataverse SQL endpoint supports a broad subset of T-SQL:: + + SELECT / SELECT DISTINCT / SELECT TOP N (0-5000) + FROM table [alias] + INNER JOIN / LEFT JOIN (multi-table, no depth limit) + WHERE (=, !=, >, <, >=, <=, LIKE, IN, NOT IN, IS NULL, + IS NOT NULL, BETWEEN, AND, OR, nested parentheses) + GROUP BY column + ORDER BY column [ASC|DESC] + OFFSET n ROWS FETCH NEXT m ROWS ONLY + COUNT(*), SUM(), AVG(), MIN(), MAX() + + ``SELECT *`` is not supported -- specify column names explicitly. + Use :meth:`sql_columns` to discover available column names for a table. + + Not supported: SELECT *, subqueries, CTE, HAVING, UNION, + RIGHT/FULL/CROSS JOIN, CASE, COALESCE, window functions, + string/date/math functions, INSERT/UPDATE/DELETE. For writes, use + ``client.records`` methods. + + :param sql: Supported SQL SELECT statement. + :type sql: :class:`str` + + :return: List of :class:`~PowerPlatform.Dataverse.models.record.Record` + objects. Returns an empty list when no rows match. + :rtype: list[~PowerPlatform.Dataverse.models.record.Record] + + :raises ~PowerPlatform.Dataverse.core.errors.ValidationError: + If ``sql`` is not a string or is empty. + + Example: + Basic query:: + + rows = await client.query.sql( + "SELECT TOP 10 name FROM account ORDER BY name" + ) + + JOIN with aggregation:: + + rows = await client.query.sql( + "SELECT a.name, COUNT(c.contactid) as cnt " + "FROM account a " + "JOIN contact c ON a.accountid = c.parentcustomerid " + "GROUP BY a.name" + ) + """ + async with self._client._scoped_odata() as od: + rows = await od._query_sql(sql) + return [Record.from_api_response("", row) for row in rows] + + # --------------------------------------------------------------- sql_columns + + async def sql_columns( + self, + table: str, + *, + include_system: bool = False, + ) -> List[Dict[str, Any]]: + """Return a simplified list of SQL-usable columns for a table. + + Each dict contains ``name`` (logical name for SQL), ``type`` + (Dataverse attribute type), ``is_pk`` (primary key flag), and + ``label`` (display name). Virtual columns are always excluded + because the SQL endpoint cannot query them. + + :param table: Schema name of the table (e.g. ``"account"``). + :type table: :class:`str` + :param include_system: When ``False`` (default), columns that end + with common system suffixes (``_base``, ``versionnumber``, + ``timezoneruleversionnumber``, ``utcconversiontimezonecode``, + ``importsequencenumber``, ``overriddencreatedon``) are excluded. + :type include_system: :class:`bool` + + :return: List of column metadata dicts. + :rtype: list[dict[str, typing.Any]] + + Example:: + + cols = await client.query.sql_columns("account") + for c in cols: + print(f"{c['name']:30s} {c['type']:20s} PK={c['is_pk']}") + """ + _SYSTEM_SUFFIXES = ( + "_base", + "versionnumber", + "timezoneruleversionnumber", + "utcconversiontimezonecode", + "importsequencenumber", + "overriddencreatedon", + ) + + raw = await self._client.tables.list_columns( + table, + select=[ + "LogicalName", + "SchemaName", + "AttributeType", + "IsPrimaryId", + "IsPrimaryName", + "DisplayName", + "AttributeOf", + ], + filter="AttributeType ne 'Virtual'", + ) + result: List[Dict[str, Any]] = [] + for c in raw: + name = c.get("LogicalName", "") + if not name: + continue + if not include_system and any(name.endswith(s) for s in _SYSTEM_SUFFIXES): + continue + # Skip computed display-name columns (AttributeOf is set, meaning + # they are auto-generated from a lookup column) + if c.get("AttributeOf"): + continue + # Extract display label + label = "" + dn = c.get("DisplayName") + if isinstance(dn, dict): + ul = dn.get("UserLocalizedLabel") + if isinstance(ul, dict): + label = ul.get("Label", "") + result.append( + { + "name": name, + "type": c.get("AttributeType", ""), + "is_pk": bool(c.get("IsPrimaryId")), + "is_name": bool(c.get("IsPrimaryName")), + "label": label, + } + ) + result.sort(key=lambda x: (not x["is_pk"], not x["is_name"], x["name"])) + return result + + # ========================================================================= + # OData helpers -- discover columns, navigation properties, and bind values + # ========================================================================= + + # ------------------------------------------------------- odata_expands + + async def odata_expands( + self, + table: str, + ) -> List[Dict[str, Any]]: + """Discover all ``$expand`` navigation properties from a table. + + Returns entries for each outgoing lookup (single-valued navigation + property). Each entry contains the exact PascalCase navigation + property name needed for ``$expand`` and ``@odata.bind``, plus + the target entity set name. + + :param table: Schema name of the table (e.g. ``"contact"``). + :type table: :class:`str` + + :return: List of dicts, each with: + + - ``nav_property`` -- PascalCase navigation property for $expand + - ``target_table`` -- target entity logical name + - ``target_entity_set`` -- target entity set (for @odata.bind) + - ``lookup_attribute`` -- the lookup column logical name + - ``relationship`` -- relationship schema name + + :rtype: list[dict[str, typing.Any]] + + Example:: + + expands = await client.query.odata_expands("contact") + for e in expands: + print(f"expand={e['nav_property']} -> {e['target_table']}") + + # Use in a query + e = next(e for e in expands if e['target_table'] == 'account') + records = await client.records.list("contact", + select=["fullname"], + expand=[e['nav_property']]) + """ + table_lower = table.lower() + rels = await self._client.tables.list_table_relationships(table) + + result: List[Dict[str, Any]] = [] + for r in rels: + ref_entity = (r.get("ReferencingEntity") or "").lower() + if ref_entity != table_lower: + continue + nav_prop = r.get("ReferencingEntityNavigationPropertyName", "") + target = r.get("ReferencedEntity", "") + lookup_attr = r.get("ReferencingAttribute", "") + schema = r.get("SchemaName", "") + if not nav_prop or not target: + continue + + # Resolve entity set name for @odata.bind + target_set = "" + try: + async with self._client._scoped_odata() as od: + target_set = await od._entity_set_from_schema_name(target) + except (KeyError, AttributeError, ValueError, MetadataError): + pass # Entity set resolution failed; target_set stays empty + + result.append( + { + "nav_property": nav_prop, + "target_table": target, + "target_entity_set": target_set, + "lookup_attribute": lookup_attr, + "relationship": schema, + } + ) + + result.sort(key=lambda x: (x["target_table"], x["nav_property"])) + return result diff --git a/src/PowerPlatform/Dataverse/aio/operations/async_records.py b/src/PowerPlatform/Dataverse/aio/operations/async_records.py new file mode 100644 index 00000000..1a2d86cf --- /dev/null +++ b/src/PowerPlatform/Dataverse/aio/operations/async_records.py @@ -0,0 +1,522 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +"""Async record CRUD operations namespace for the Dataverse SDK.""" + +from __future__ import annotations + +from typing import Any, AsyncGenerator, Dict, List, Optional, Union, overload, TYPE_CHECKING + +from ...core.errors import HttpError +from ...models.record import QueryResult, Record +from ...models.upsert import UpsertItem + +if TYPE_CHECKING: + from ...models.filters import FilterExpression + from ..async_client import AsyncDataverseClient + + +__all__ = ["AsyncRecordOperations"] + + +class AsyncRecordOperations: + """Async namespace for record-level CRUD operations. + + Accessed via ``client.records``. Provides create, update, delete, retrieve, + list, and upsert operations on individual Dataverse records. + + :param client: The parent :class:`~PowerPlatform.Dataverse.aio.async_client.AsyncDataverseClient` instance. + :type client: ~PowerPlatform.Dataverse.aio.async_client.AsyncDataverseClient + + Example:: + + async with AsyncDataverseClient(base_url, credential) as client: + + # Create a single record + guid = await client.records.create("account", {"name": "Contoso Ltd"}) + + # Retrieve a record + record = await client.records.retrieve("account", guid, select=["name"]) + + # Update a record + await client.records.update("account", guid, {"telephone1": "555-0100"}) + + # Delete a record + await client.records.delete("account", guid) + """ + + def __init__(self, client: "AsyncDataverseClient") -> None: + self._client = client + + # ------------------------------------------------------------------ create + + @overload + async def create(self, table: str, data: Dict[str, Any]) -> str: ... + + @overload + async def create(self, table: str, data: List[Dict[str, Any]]) -> List[str]: ... + + async def create( + self, + table: str, + data: Union[Dict[str, Any], List[Dict[str, Any]]], + ) -> Union[str, List[str]]: + """Create one or more records in a Dataverse table. + + When ``data`` is a single dictionary, creates one record and returns its + GUID as a string. When ``data`` is a list of dictionaries, creates all + records via the ``CreateMultiple`` action and returns a list of GUIDs. + + :param table: Schema name of the table (e.g. ``"account"`` or ``"new_MyTestTable"``). + :type table: :class:`str` + :param data: A single record dictionary or a list of record dictionaries. + Each dictionary maps column schema names to values. + :type data: dict or list[dict] + + :return: A single GUID string for a single record, or a list of GUID + strings for bulk creation. + :rtype: str or list[str] + + :raises TypeError: If ``data`` is not a dict or list[dict]. + + Example: + Create a single record:: + + guid = await client.records.create("account", {"name": "Contoso"}) + print(f"Created: {guid}") + + Create multiple records:: + + guids = await client.records.create("account", [ + {"name": "Contoso"}, + {"name": "Fabrikam"}, + ]) + print(f"Created {len(guids)} accounts") + """ + async with self._client._scoped_odata() as od: + entity_set = await od._entity_set_from_schema_name(table) + if isinstance(data, dict): + rid = await od._create(entity_set, table, data) + if not isinstance(rid, str): + raise TypeError("_create (single) did not return GUID string") + return rid + if isinstance(data, list): + ids = await od._create_multiple(entity_set, table, data) + if not isinstance(ids, list) or not all(isinstance(x, str) for x in ids): + raise TypeError("_create (multi) did not return list[str]") + return ids + raise TypeError("data must be dict or list[dict]") + + # ------------------------------------------------------------------ update + + async def update( + self, + table: str, + ids: Union[str, List[str]], + changes: Union[Dict[str, Any], List[Dict[str, Any]]], + ) -> None: + """Update one or more records in a Dataverse table. + + Supports three usage patterns: + + 1. **Single** -- ``update("account", "guid", {"name": "New"})`` + 2. **Broadcast** -- ``update("account", [id1, id2], {"status": 1})`` + applies the same changes dict to every ID. + 3. **Paired** -- ``update("account", [id1, id2], [ch1, ch2])`` + applies each changes dict to its corresponding ID (lists must be + equal length). + + :param table: Schema name of the table (e.g. ``"account"``). + :type table: :class:`str` + :param ids: A single GUID string, or a list of GUID strings. + :type ids: str or list[str] + :param changes: A dictionary of field changes (single/broadcast), or a + list of dictionaries (paired, one per ID). + :type changes: dict or list[dict] + + :raises TypeError: If ``ids`` is not str or list[str], or if ``changes`` + does not match the expected pattern. + + Example: + Single update:: + + await client.records.update("account", account_id, {"telephone1": "555-0100"}) + + Broadcast update:: + + await client.records.update("account", [id1, id2], {"statecode": 1}) + + Paired update:: + + await client.records.update( + "account", + [id1, id2], + [{"name": "Name A"}, {"name": "Name B"}], + ) + """ + async with self._client._scoped_odata() as od: + if isinstance(ids, str): + if not isinstance(changes, dict): + raise TypeError("For single id, changes must be a dict") + await od._update(table, ids, changes) + return None + if not isinstance(ids, list): + raise TypeError("ids must be str or list[str]") + await od._update_by_ids(table, ids, changes) + return None + + # ------------------------------------------------------------------ delete + + @overload + async def delete(self, table: str, ids: str) -> None: ... + + @overload + async def delete(self, table: str, ids: List[str], *, use_bulk_delete: bool = True) -> Optional[str]: ... + + async def delete( + self, + table: str, + ids: Union[str, List[str]], + *, + use_bulk_delete: bool = True, + ) -> Optional[str]: + """Delete one or more records from a Dataverse table. + + When ``ids`` is a single string, deletes that one record. When ``ids`` + is a list, either executes a BulkDelete action (returning the async job + ID) or deletes each record sequentially depending on ``use_bulk_delete``. + + :param table: Schema name of the table (e.g. ``"account"``). + :type table: :class:`str` + :param ids: A single GUID string, or a list of GUID strings. + :type ids: str or list[str] + :param use_bulk_delete: When True (default) and ``ids`` is a list, use + the BulkDelete action and return its async job ID. When False, delete + records one at a time. + :type use_bulk_delete: :class:`bool` + + :return: The BulkDelete job ID when bulk-deleting; otherwise None. + :rtype: :class:`str` or None + + :raises TypeError: If ``ids`` is not str or list[str]. + + Example: + Delete a single record:: + + await client.records.delete("account", account_id) + + Bulk delete:: + + job_id = await client.records.delete("account", [id1, id2, id3]) + """ + async with self._client._scoped_odata() as od: + if isinstance(ids, str): + await od._delete(table, ids) + return None + if not isinstance(ids, list): + raise TypeError("ids must be str or list[str]") + if not ids: + return None + if not all(isinstance(rid, str) for rid in ids): + raise TypeError("ids must contain string GUIDs") + if use_bulk_delete: + return await od._delete_multiple(table, ids) + for rid in ids: + await od._delete(table, rid) + return None + + # --------------------------------------------------------------- retrieve + + async def retrieve( + self, + table: str, + record_id: str, + *, + select: Optional[List[str]] = None, + expand: Optional[List[str]] = None, + include_annotations: Optional[str] = None, + ) -> Optional[Record]: + """Fetch a single record by its GUID, returning ``None`` if not found. + + Returns ``None`` instead of raising when the record does not exist (HTTP 404). + + :param table: Schema name of the table (e.g. ``"account"``). + :type table: :class:`str` + :param record_id: GUID of the record to retrieve. + :type record_id: :class:`str` + :param select: Optional list of column logical names to include. + :type select: list[str] or None + :param expand: Optional list of navigation properties to expand (e.g. + ``["primarycontactid"]``). Navigation property names are + case-sensitive and must match the entity's ``$metadata``. + :type expand: list[str] or None + :param include_annotations: OData annotation pattern for the + ``Prefer: odata.include-annotations`` header (e.g. ``"*"`` or + ``"OData.Community.Display.V1.FormattedValue"``), or ``None``. + :type include_annotations: :class:`str` or None + :return: Typed record, or ``None`` if not found. + :rtype: :class:`~PowerPlatform.Dataverse.models.record.Record` or None + + Example:: + + record = await client.records.retrieve( + "account", account_id, + select=["name", "statuscode"], + expand=["primarycontactid"], + include_annotations="OData.Community.Display.V1.FormattedValue", + ) + if record is not None: + contact = record.get("primarycontactid") or {} + print(contact.get("fullname")) + """ + async with self._client._scoped_odata() as od: + try: + raw = await od._get( + table, record_id, select=select, expand=expand, include_annotations=include_annotations + ) + except HttpError as exc: + if exc.status_code == 404: + return None + raise + return Record.from_api_response(table, raw, record_id=record_id) + + # -------------------------------------------------------------------- list + + async def list( + self, + table: str, + *, + filter: Optional[Union[str, "FilterExpression"]] = None, + select: Optional[List[str]] = None, + orderby: Optional[List[str]] = None, + top: Optional[int] = None, + expand: Optional[List[str]] = None, + page_size: Optional[int] = None, + count: bool = False, + include_annotations: Optional[str] = None, + ) -> QueryResult: + """Fetch multiple records and return them as a :class:`QueryResult`. + + All pages are collected eagerly and returned as a single :class:`QueryResult`. + + :param table: Schema name of the table (e.g. ``"account"``). + :type table: :class:`str` + :param filter: Optional OData filter string or :class:`FilterExpression`. + :type filter: str or FilterExpression or None + :param select: Optional list of column logical names to include. + :type select: list[str] or None + :param orderby: Optional list of sort expressions (e.g. ``["name asc", "createdon desc"]``). + :type orderby: list[str] or None + :param top: Maximum total number of records to return. + :type top: int or None + :param expand: Optional list of navigation properties to expand. + :type expand: list[str] or None + :param page_size: Per-page size hint via ``Prefer: odata.maxpagesize``. + :type page_size: int or None + :param count: If ``True``, adds ``$count=true`` to include a total record count. + :type count: bool + :param include_annotations: OData annotation pattern for the + ``Prefer: odata.include-annotations`` header, or ``None``. + :type include_annotations: :class:`str` or None + :return: All matching records collected into a :class:`QueryResult`. + :rtype: :class:`~PowerPlatform.Dataverse.models.record.QueryResult` + + Example:: + + from PowerPlatform.Dataverse import col + + result = await client.records.list( + "account", + filter=col("statecode") == 0, + select=["name", "statuscode"], + orderby=["name asc"], + top=100, + include_annotations="OData.Community.Display.V1.FormattedValue", + ) + for record in result: + print(record["name"], record.get("statuscode@OData.Community.Display.V1.FormattedValue")) + """ + filter_str: Optional[str] = str(filter) if filter is not None else None + all_records: List[Record] = [] + async with self._client._scoped_odata() as od: + async for page in od._get_multiple( + table, + select=select, + filter=filter_str, + orderby=orderby, + top=top, + expand=expand, + page_size=page_size, + count=count, + include_annotations=include_annotations, + ): + all_records.extend(Record.from_api_response(table, row) for row in page) + return QueryResult(all_records) + + # --------------------------------------------------------------- list_pages + + async def list_pages( + self, + table: str, + *, + filter: Optional[Union[str, "FilterExpression"]] = None, + select: Optional[List[str]] = None, + orderby: Optional[List[str]] = None, + top: Optional[int] = None, + expand: Optional[List[str]] = None, + page_size: Optional[int] = None, + count: bool = False, + include_annotations: Optional[str] = None, + ) -> AsyncGenerator[QueryResult, None]: + """Lazily yield one :class:`QueryResult` per HTTP page. + + Streaming counterpart to :meth:`list` — use when you want to process + records page by page without loading all into memory. Each iteration + triggers one network request via ``@odata.nextLink``. One-shot — do + not iterate more than once. + + :param table: Schema name of the table (e.g. ``"account"``). + :type table: :class:`str` + :param filter: Optional OData filter string or :class:`FilterExpression`. + :type filter: str or FilterExpression or None + :param select: Optional list of column logical names to include. + :type select: list[str] or None + :param orderby: Optional list of sort expressions. + :type orderby: list[str] or None + :param top: Maximum total number of records to return. + :type top: int or None + :param expand: Optional list of navigation properties to expand. + :type expand: list[str] or None + :param page_size: Per-page size hint via ``Prefer: odata.maxpagesize``. + :type page_size: int or None + :param count: If ``True``, adds ``$count=true`` to include a total record count. + :type count: bool + :param include_annotations: OData annotation pattern for the + ``Prefer: odata.include-annotations`` header, or ``None``. + :type include_annotations: :class:`str` or None + :return: Async generator of per-page :class:`QueryResult` objects. + :rtype: AsyncGenerator[:class:`~PowerPlatform.Dataverse.models.record.QueryResult`, None] + + Example:: + + async for page in client.records.list_pages( + "account", + filter="statecode eq 0", + orderby=["name asc"], + page_size=200, + ): + process(page.to_dataframe()) + """ + filter_str: Optional[str] = str(filter) if filter is not None else None + async with self._client._scoped_odata() as od: + async for page in od._get_multiple( + table, + select=select, + filter=filter_str, + orderby=orderby, + top=top, + expand=expand, + page_size=page_size, + count=count, + include_annotations=include_annotations, + ): + yield QueryResult([Record.from_api_response(table, row) for row in page]) + + # ------------------------------------------------------------------ upsert + + async def upsert(self, table: str, items: List[Union[UpsertItem, Dict[str, Any]]]) -> None: + """Upsert one or more records identified by alternate keys. + + When ``items`` contains a single entry, performs a single upsert via PATCH + using the alternate key in the URL. When ``items`` contains multiple entries, + uses the ``UpsertMultiple`` bulk action. + + Each item must be either a :class:`~PowerPlatform.Dataverse.models.upsert.UpsertItem` + or a plain ``dict`` with ``"alternate_key"`` and ``"record"`` keys (both dicts). + + :param table: Schema name of the table (e.g. ``"account"`` or ``"new_MyTestTable"``). + :type table: str + :param items: Non-empty list of :class:`~PowerPlatform.Dataverse.models.upsert.UpsertItem` + instances or dicts with ``"alternate_key"`` and ``"record"`` keys. + :type items: list[UpsertItem | dict] + + :return: ``None`` + :rtype: None + + :raises TypeError: If ``items`` is not a non-empty list, or if any element is + neither a :class:`~PowerPlatform.Dataverse.models.upsert.UpsertItem` nor a + dict with ``"alternate_key"`` and ``"record"`` keys. + + Example: + Upsert a single record using ``UpsertItem``:: + + from PowerPlatform.Dataverse.models.upsert import UpsertItem + + await client.records.upsert("account", [ + UpsertItem( + alternate_key={"accountnumber": "ACC-001"}, + record={"name": "Contoso Ltd", "description": "Primary account"}, + ) + ]) + + Upsert a single record using a plain dict:: + + await client.records.upsert("account", [ + { + "alternate_key": {"accountnumber": "ACC-001"}, + "record": {"name": "Contoso Ltd", "description": "Primary account"}, + }, + ]) + + Upsert multiple records using ``UpsertItem``:: + + from PowerPlatform.Dataverse.models.upsert import UpsertItem + + await client.records.upsert("account", [ + UpsertItem( + alternate_key={"accountnumber": "ACC-001"}, + record={"name": "Contoso Ltd", "description": "Primary account"}, + ), + UpsertItem( + alternate_key={"accountnumber": "ACC-002"}, + record={"name": "Fabrikam Inc", "description": "Partner account"}, + ), + ]) + + Upsert multiple records using plain dicts:: + + await client.records.upsert("account", [ + { + "alternate_key": {"accountnumber": "ACC-001"}, + "record": {"name": "Contoso Ltd", "description": "Primary account"}, + }, + { + "alternate_key": {"accountnumber": "ACC-002"}, + "record": {"name": "Fabrikam Inc", "description": "Partner account"}, + }, + ]) + + The ``alternate_key`` dict may contain multiple columns when the configured + alternate key is composite, e.g. + ``{"accountnumber": "ACC-001", "address1_postalcode": "98052"}``. + """ + if not isinstance(items, list) or not items: + raise TypeError("items must be a non-empty list of UpsertItem or dicts") + normalized: List[UpsertItem] = [] + for i in items: + if isinstance(i, UpsertItem): + normalized.append(i) + elif isinstance(i, dict) and isinstance(i.get("alternate_key"), dict) and isinstance(i.get("record"), dict): + normalized.append(UpsertItem(alternate_key=i["alternate_key"], record=i["record"])) + else: + raise TypeError("Each item must be an UpsertItem or a dict with 'alternate_key' and 'record' keys") + async with self._client._scoped_odata() as od: + entity_set = await od._entity_set_from_schema_name(table) + if len(normalized) == 1: + item = normalized[0] + await od._upsert(entity_set, table, item.alternate_key, item.record) + else: + alternate_keys = [i.alternate_key for i in normalized] + records = [i.record for i in normalized] + await od._upsert_multiple(entity_set, table, alternate_keys, records) + return None diff --git a/src/PowerPlatform/Dataverse/aio/operations/async_tables.py b/src/PowerPlatform/Dataverse/aio/operations/async_tables.py new file mode 100644 index 00000000..0fbe61c8 --- /dev/null +++ b/src/PowerPlatform/Dataverse/aio/operations/async_tables.py @@ -0,0 +1,838 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +"""Async table metadata operations namespace for the Dataverse SDK.""" + +from __future__ import annotations + +from typing import Any, Dict, List, Optional, Union, TYPE_CHECKING + +from ...models.relationship import ( + LookupAttributeMetadata, + OneToManyRelationshipMetadata, + ManyToManyRelationshipMetadata, + RelationshipInfo, +) +from ...models.table_info import AlternateKeyInfo +from ...models.labels import Label, LocalizedLabel +from ...models.table_info import TableInfo +from ...common.constants import CASCADE_BEHAVIOR_REMOVE_LINK + +if TYPE_CHECKING: + from ..async_client import AsyncDataverseClient + + +__all__ = ["AsyncTableOperations"] + + +class AsyncTableOperations: + """Async namespace for table-level metadata operations. + + Accessed via ``client.tables``. Provides operations to create, delete, + inspect, and list Dataverse tables, as well as add and remove columns. + + :param client: The parent :class:`~PowerPlatform.Dataverse.aio.async_client.AsyncDataverseClient` instance. + :type client: ~PowerPlatform.Dataverse.aio.async_client.AsyncDataverseClient + + Example:: + + async with AsyncDataverseClient(base_url, credential) as client: + + # Create a table + info = await client.tables.create( + "new_Product", + {"new_Price": "decimal", "new_InStock": "bool"}, + solution="MySolution", + ) + + # List tables + tables = await client.tables.list() + + # Get table info + info = await client.tables.get("new_Product") + + # Add columns + await client.tables.add_columns("new_Product", {"new_Rating": "int"}) + + # Remove columns + await client.tables.remove_columns("new_Product", "new_Rating") + + # Delete a table + await client.tables.delete("new_Product") + """ + + def __init__(self, client: "AsyncDataverseClient") -> None: + self._client = client + + # ----------------------------------------------------------------- create + + async def create( + self, + table: str, + columns: Dict[str, Any], + *, + solution: Optional[str] = None, + primary_column: Optional[str] = None, + display_name: Optional[str] = None, + ) -> TableInfo: + """Create a custom table with the specified columns. + + :param table: Schema name of the table with customization prefix + (e.g. ``"new_MyTestTable"``). + :type table: :class:`str` + :param columns: Mapping of column schema names (with customization + prefix) to their types. Supported types include ``"string"`` + (or ``"text"``), ``"memo"`` (or ``"multiline"``), + ``"int"`` (or ``"integer"``), ``"decimal"`` + (or ``"money"``), ``"float"`` (or ``"double"``), ``"datetime"`` + (or ``"date"``), ``"bool"`` (or ``"boolean"``), ``"file"``, and + ``Enum`` subclasses + (for local option sets). + :type columns: :class:`dict` + :param solution: Optional solution unique name that should own the new + table. When omitted the table is created in the default solution. + :type solution: :class:`str` or None + :param primary_column: Optional primary name column schema name with + customization prefix (e.g. ``"new_ProductName"``). If not provided, + defaults to ``"{prefix}_Name"``. + :type primary_column: :class:`str` or None + :param display_name: Human-readable display name for the table + (e.g. ``"Product"``). When omitted, defaults to the table schema name. + :type display_name: :class:`str` or None + + :return: Table metadata with ``schema_name``, ``entity_set_name``, + ``logical_name``, ``metadata_id``, and ``columns_created``. + Supports dict-like access with legacy keys for backward + compatibility. + :rtype: :class:`~PowerPlatform.Dataverse.models.table_info.TableInfo` + + :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: + If table creation fails or the table already exists. + + Example: + Create a table with simple columns:: + + from enum import IntEnum + + class ItemStatus(IntEnum): + ACTIVE = 1 + INACTIVE = 2 + + result = await client.tables.create( + "new_Product", + { + "new_Title": "string", + "new_Price": "decimal", + "new_Status": ItemStatus, + }, + solution="MySolution", + primary_column="new_ProductName", + display_name="Product", + ) + print(f"Created: {result['table_schema_name']}") + """ + async with self._client._scoped_odata() as od: + raw = await od._create_table( + table, + columns, + solution, + primary_column, + display_name, + ) + return TableInfo.from_dict(raw) + + # ----------------------------------------------------------------- delete + + async def delete(self, table: str) -> None: + """Delete a custom table by schema name. + + :param table: Schema name of the table (e.g. ``"new_MyTestTable"``). + :type table: :class:`str` + + :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: + If the table does not exist or deletion fails. + + .. warning:: + This operation is irreversible and will delete all records in the + table along with the table definition. + + Example:: + + await client.tables.delete("new_MyTestTable") + """ + async with self._client._scoped_odata() as od: + await od._delete_table(table) + + # -------------------------------------------------------------------- get + + async def get(self, table: str) -> Optional[TableInfo]: + """Get basic metadata for a table if it exists. + + :param table: Schema name of the table (e.g. ``"new_MyTestTable"`` + or ``"account"``). + :type table: :class:`str` + + :return: Table metadata, or ``None`` if the table is not found. + Supports dict-like access with legacy keys for backward + compatibility. + :rtype: :class:`~PowerPlatform.Dataverse.models.table_info.TableInfo` + or None + + Example:: + + info = await client.tables.get("new_MyTestTable") + if info: + print(f"Logical name: {info['table_logical_name']}") + print(f"Entity set: {info['entity_set_name']}") + """ + async with self._client._scoped_odata() as od: + raw = await od._get_table_info(table) + if raw is None: + return None + return TableInfo.from_dict(raw) + + # ------------------------------------------------------------------- list + + async def list( + self, + *, + filter: Optional[str] = None, + select: Optional[List[str]] = None, + ) -> List[Dict[str, Any]]: + """List all non-private tables in the Dataverse environment. + + By default returns every table where ``IsPrivate eq false``. Supply + an optional OData ``$filter`` expression to further narrow the results. + The expression is combined with the default ``IsPrivate eq false`` + clause using ``and``. + + :param filter: Optional OData ``$filter`` expression to further narrow + the list of returned tables (e.g. + ``"SchemaName eq 'Account'"``). Column names in filter + expressions must use the exact property names from the + ``EntityDefinitions`` metadata (typically PascalCase). + :type filter: :class:`str` or None + :param select: Optional list of property names to include in the + response (projected via the OData ``$select`` query option). + Property names must use the exact PascalCase names from the + ``EntityDefinitions`` metadata (e.g. + ``["LogicalName", "SchemaName", "DisplayName"]``). + When ``None`` (the default) or an empty list, all properties are + returned. + :type select: list[str] or None + + :return: List of EntityDefinition metadata dictionaries. + :rtype: list[dict] + + Example:: + + # List all non-private tables + tables = await client.tables.list() + for table in tables: + print(table["LogicalName"]) + + # List only tables whose schema name starts with "new_" + custom_tables = await client.tables.list( + filter="startswith(SchemaName, 'new_')" + ) + + # List tables with only specific properties + tables = await client.tables.list( + select=["LogicalName", "SchemaName", "EntitySetName"] + ) + """ + async with self._client._scoped_odata() as od: + return await od._list_tables(filter=filter, select=select) + + # ------------------------------------------------------------- add_columns + + async def add_columns( + self, + table: str, + columns: Dict[str, Any], + ) -> List[str]: + """Add one or more columns to an existing table. + + :param table: Schema name of the table (e.g. ``"new_MyTestTable"``). + :type table: :class:`str` + :param columns: Mapping of column schema names (with customization + prefix) to their types. Supported types are the same as for + :meth:`create`. + :type columns: :class:`dict` + + :return: Schema names of the columns that were created. + :rtype: list[str] + + :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: + If the table does not exist. + + Example:: + + created = await client.tables.add_columns( + "new_MyTestTable", + {"new_Notes": "string", "new_Active": "bool"}, + ) + print(created) # ['new_Notes', 'new_Active'] + """ + async with self._client._scoped_odata() as od: + return await od._create_columns(table, columns) + + # ---------------------------------------------------------- remove_columns + + async def remove_columns( + self, + table: str, + columns: Union[str, List[str]], + ) -> List[str]: + """Remove one or more columns from a table. + + :param table: Schema name of the table (e.g. ``"new_MyTestTable"``). + :type table: :class:`str` + :param columns: Column schema name or list of column schema names to + remove. Must include the customization prefix (e.g. + ``"new_TestColumn"``). + :type columns: str or list[str] + + :return: Schema names of the columns that were removed. + :rtype: list[str] + + :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: + If the table or a specified column does not exist. + + Example:: + + removed = await client.tables.remove_columns( + "new_MyTestTable", + ["new_Notes", "new_Active"], + ) + print(removed) # ['new_Notes', 'new_Active'] + """ + async with self._client._scoped_odata() as od: + return await od._delete_columns(table, columns) + + # ------------------------------------------------------ create_one_to_many + + async def create_one_to_many_relationship( + self, + lookup: LookupAttributeMetadata, + relationship: OneToManyRelationshipMetadata, + *, + solution: Optional[str] = None, + ) -> RelationshipInfo: + """Create a one-to-many relationship between tables. + + This operation creates both the relationship and the lookup attribute + on the referencing table. + + :param lookup: Metadata defining the lookup attribute. + :type lookup: ~PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata + :param relationship: Metadata defining the relationship. + :type relationship: ~PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata + :param solution: Optional solution unique name to add relationship to. + :type solution: :class:`str` or None + + :return: Relationship metadata with ``relationship_id``, + ``relationship_schema_name``, ``relationship_type``, + ``lookup_schema_name``, ``referenced_entity``, and + ``referencing_entity``. + :rtype: :class:`~PowerPlatform.Dataverse.models.relationship.RelationshipInfo` + + :raises ~PowerPlatform.Dataverse.core.errors.HttpError: + If the Web API request fails. + + Example: + Create a one-to-many relationship: Department (1) -> Employee (N):: + + from PowerPlatform.Dataverse.models.relationship import ( + LookupAttributeMetadata, + OneToManyRelationshipMetadata, + Label, + LocalizedLabel, + CascadeConfiguration, + ) + from PowerPlatform.Dataverse.common.constants import ( + CASCADE_BEHAVIOR_REMOVE_LINK, + ) + + lookup = LookupAttributeMetadata( + schema_name="new_DepartmentId", + display_name=Label( + localized_labels=[ + LocalizedLabel(label="Department", language_code=1033) + ] + ), + ) + + relationship = OneToManyRelationshipMetadata( + schema_name="new_Department_Employee", + referenced_entity="new_department", + referencing_entity="new_employee", + referenced_attribute="new_departmentid", + cascade_configuration=CascadeConfiguration( + delete=CASCADE_BEHAVIOR_REMOVE_LINK, + ), + ) + + result = await client.tables.create_one_to_many_relationship(lookup, relationship) + print(f"Created lookup field: {result.lookup_schema_name}") + """ + async with self._client._scoped_odata() as od: + raw = await od._create_one_to_many_relationship( + lookup, + relationship, + solution, + ) + return RelationshipInfo.from_one_to_many( + relationship_id=raw["relationship_id"], + relationship_schema_name=raw["relationship_schema_name"], + lookup_schema_name=raw["lookup_schema_name"], + referenced_entity=raw["referenced_entity"], + referencing_entity=raw["referencing_entity"], + ) + + # ----------------------------------------------------- create_many_to_many + + async def create_many_to_many_relationship( + self, + relationship: ManyToManyRelationshipMetadata, + *, + solution: Optional[str] = None, + ) -> RelationshipInfo: + """Create a many-to-many relationship between tables. + + This operation creates a many-to-many relationship and an intersect + table to manage the relationship. + + :param relationship: Metadata defining the many-to-many relationship. + :type relationship: ~PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata + :param solution: Optional solution unique name to add relationship to. + :type solution: :class:`str` or None + + :return: Relationship metadata with ``relationship_id``, + ``relationship_schema_name``, ``relationship_type``, + ``entity1_logical_name``, and ``entity2_logical_name``. + :rtype: :class:`~PowerPlatform.Dataverse.models.relationship.RelationshipInfo` + + :raises ~PowerPlatform.Dataverse.core.errors.HttpError: + If the Web API request fails. + + Example: + Create a many-to-many relationship: Employee <-> Project:: + + from PowerPlatform.Dataverse.models.relationship import ( + ManyToManyRelationshipMetadata, + ) + + relationship = ManyToManyRelationshipMetadata( + schema_name="new_employee_project", + entity1_logical_name="new_employee", + entity2_logical_name="new_project", + ) + + result = await client.tables.create_many_to_many_relationship(relationship) + print(f"Created: {result.relationship_schema_name}") + """ + async with self._client._scoped_odata() as od: + raw = await od._create_many_to_many_relationship( + relationship, + solution, + ) + return RelationshipInfo.from_many_to_many( + relationship_id=raw["relationship_id"], + relationship_schema_name=raw["relationship_schema_name"], + entity1_logical_name=raw["entity1_logical_name"], + entity2_logical_name=raw["entity2_logical_name"], + ) + + # ------------------------------------------------------- delete_relationship + + async def delete_relationship(self, relationship_id: str) -> None: + """Delete a relationship by its metadata ID. + + :param relationship_id: The GUID of the relationship metadata. + :type relationship_id: :class:`str` + + :raises ~PowerPlatform.Dataverse.core.errors.HttpError: + If the Web API request fails. + + .. warning:: + Deleting a relationship also removes the associated lookup attribute + for one-to-many relationships. This operation is irreversible. + + Example:: + + await client.tables.delete_relationship( + "12345678-1234-1234-1234-123456789abc" + ) + """ + async with self._client._scoped_odata() as od: + await od._delete_relationship(relationship_id) + + # -------------------------------------------------------- get_relationship + + async def get_relationship(self, schema_name: str) -> Optional[RelationshipInfo]: + """Retrieve relationship metadata by schema name. + + :param schema_name: The schema name of the relationship. + :type schema_name: :class:`str` + + :return: Relationship metadata, or ``None`` if not found. + :rtype: :class:`~PowerPlatform.Dataverse.models.relationship.RelationshipInfo` + or None + + :raises ~PowerPlatform.Dataverse.core.errors.HttpError: + If the Web API request fails. + + Example:: + + rel = await client.tables.get_relationship("new_Department_Employee") + if rel: + print(f"Found: {rel.relationship_schema_name}") + """ + async with self._client._scoped_odata() as od: + raw = await od._get_relationship(schema_name) + if raw is None: + return None + return RelationshipInfo.from_api_response(raw) + + # ------------------------------------------------------- create_lookup_field + + async def create_lookup_field( + self, + referencing_table: str, + lookup_field_name: str, + referenced_table: str, + *, + display_name: Optional[str] = None, + description: Optional[str] = None, + required: bool = False, + cascade_delete: str = CASCADE_BEHAVIOR_REMOVE_LINK, + solution: Optional[str] = None, + language_code: int = 1033, + ) -> RelationshipInfo: + """Create a simple lookup field relationship. + + This is a convenience method that wraps :meth:`create_one_to_many_relationship` + for the common case of adding a lookup field to an existing table. + + :param referencing_table: Logical name of the table that will have + the lookup field (child table). + :type referencing_table: :class:`str` + :param lookup_field_name: Schema name for the lookup field + (e.g., ``"new_AccountId"``). + :type lookup_field_name: :class:`str` + :param referenced_table: Logical name of the table being referenced + (parent table). + :type referenced_table: :class:`str` + :param display_name: Display name for the lookup field. Defaults to + the referenced table name. + :type display_name: :class:`str` or None + :param description: Optional description for the lookup field. + :type description: :class:`str` or None + :param required: Whether the lookup is required. Defaults to ``False``. + :type required: :class:`bool` + :param cascade_delete: Delete behavior (``"RemoveLink"``, + ``"Cascade"``, ``"Restrict"``). Defaults to ``"RemoveLink"``. + :type cascade_delete: :class:`str` + :param solution: Optional solution unique name to add the relationship + to. + :type solution: :class:`str` or None + :param language_code: Language code for labels. Defaults to 1033 + (English). + :type language_code: :class:`int` + + :return: Relationship metadata with ``relationship_id``, + ``relationship_schema_name``, ``relationship_type``, + ``lookup_schema_name``, ``referenced_entity``, and + ``referencing_entity``. + :rtype: :class:`~PowerPlatform.Dataverse.models.relationship.RelationshipInfo` + + :raises ~PowerPlatform.Dataverse.core.errors.HttpError: + If the Web API request fails. + + Example: + Create a simple lookup field:: + + result = await client.tables.create_lookup_field( + referencing_table="new_order", + lookup_field_name="new_AccountId", + referenced_table="account", + display_name="Account", + required=True, + cascade_delete=CASCADE_BEHAVIOR_REMOVE_LINK, + ) + print(f"Created lookup: {result['lookup_schema_name']}") + """ + async with self._client._scoped_odata() as od: + lookup, relationship = od._build_lookup_field_models( + referencing_table=referencing_table, + lookup_field_name=lookup_field_name, + referenced_table=referenced_table, + display_name=display_name, + description=description, + required=required, + cascade_delete=cascade_delete, + language_code=language_code, + ) + + return await self.create_one_to_many_relationship(lookup, relationship, solution=solution) + + # ------------------------------------------------- create_alternate_key + + async def create_alternate_key( + self, + table: str, + key_name: str, + columns: List[str], + *, + display_name: Optional[str] = None, + language_code: int = 1033, + ) -> AlternateKeyInfo: + """Create an alternate key on a table. + + Alternate keys allow upsert operations to identify records by one or + more columns instead of the primary GUID. After creation the key is + queued for index building; its :attr:`~AlternateKeyInfo.status` will + transition from ``"Pending"`` to ``"Active"`` once the index is ready. + + :param table: Schema name of the table (e.g. ``"new_Product"``). + :type table: :class:`str` + :param key_name: Schema name for the new alternate key + (e.g. ``"new_product_code_key"``). + :type key_name: :class:`str` + :param columns: List of column logical names that compose the key + (e.g. ``["new_productcode"]``). + :type columns: list[str] + :param display_name: Display name for the key. Defaults to + ``key_name`` if not provided. + :type display_name: :class:`str` or None + :param language_code: Language code for labels. Defaults to 1033 + (English). + :type language_code: :class:`int` + + :return: Metadata for the newly created alternate key. + :rtype: :class:`~PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo` + + :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: + If the table does not exist. + :raises ~PowerPlatform.Dataverse.core.errors.HttpError: + If the Web API request fails. + + Example: + Create a single-column alternate key for upsert:: + + key = await client.tables.create_alternate_key( + "new_Product", + "new_product_code_key", + ["new_productcode"], + display_name="Product Code", + ) + print(f"Key ID: {key.metadata_id}") + print(f"Columns: {key.key_attributes}") + """ + label = Label(localized_labels=[LocalizedLabel(label=display_name or key_name, language_code=language_code)]) + async with self._client._scoped_odata() as od: + raw = await od._create_alternate_key(table, key_name, columns, label) + return AlternateKeyInfo( + metadata_id=raw["metadata_id"], + schema_name=raw["schema_name"], + key_attributes=raw["key_attributes"], + status="Pending", + ) + + # --------------------------------------------------- get_alternate_keys + + async def get_alternate_keys(self, table: str) -> List[AlternateKeyInfo]: + """List all alternate keys defined on a table. + + :param table: Schema name of the table (e.g. ``"new_Product"``). + :type table: :class:`str` + + :return: List of alternate key metadata objects. May be empty if no + alternate keys are defined. + :rtype: list[~PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo] + + :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: + If the table does not exist. + :raises ~PowerPlatform.Dataverse.core.errors.HttpError: + If the Web API request fails. + + Example: + List alternate keys and print their status:: + + keys = await client.tables.get_alternate_keys("new_Product") + for key in keys: + print(f"{key.schema_name}: {key.status}") + """ + async with self._client._scoped_odata() as od: + raw_list = await od._get_alternate_keys(table) + return [AlternateKeyInfo.from_api_response(item) for item in raw_list] + + # ------------------------------------------------ delete_alternate_key + + async def delete_alternate_key(self, table: str, key_id: str) -> None: + """Delete an alternate key by its metadata ID. + + :param table: Schema name of the table (e.g. ``"new_Product"``). + :type table: :class:`str` + :param key_id: Metadata GUID of the alternate key to delete. + :type key_id: :class:`str` + + :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: + If the table does not exist. + :raises ~PowerPlatform.Dataverse.core.errors.HttpError: + If the Web API request fails. + + .. warning:: + Deleting an alternate key that is in use by upsert operations will + cause those operations to fail. This operation is irreversible. + + Example:: + + await client.tables.delete_alternate_key( + "new_Product", + "12345678-1234-1234-1234-123456789abc", + ) + """ + async with self._client._scoped_odata() as od: + await od._delete_alternate_key(table, key_id) + + # -------------------------------------------------------- list_columns + + async def list_columns( + self, + table: str, + *, + select: Optional[List[str]] = None, + filter: Optional[str] = None, + ) -> List[Dict[str, Any]]: + """List all attribute (column) definitions for a table. + + :param table: Schema name of the table (e.g. ``"account"`` or + ``"new_Product"``). + :type table: :class:`str` + :param select: Optional list of property names to project via + ``$select``. Values are passed as-is (PascalCase). + :type select: list[str] or None + :param filter: Optional OData ``$filter`` expression. For example, + ``"AttributeType eq 'String'"`` returns only string columns. + :type filter: :class:`str` or None + + :return: List of raw attribute metadata dictionaries. + :rtype: list[dict[str, typing.Any]] + + :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: + If the table is not found. + :raises ~PowerPlatform.Dataverse.core.errors.HttpError: + If the Web API request fails. + + Example:: + + # List all columns on the account table + columns = await client.tables.list_columns("account") + for col in columns: + print(f"{col['LogicalName']} ({col.get('AttributeType')})") + + # List only specific properties + columns = await client.tables.list_columns( + "account", + select=["LogicalName", "SchemaName", "AttributeType"], + ) + + # Filter to only string attributes + columns = await client.tables.list_columns( + "account", + filter="AttributeType eq 'String'", + ) + """ + async with self._client._scoped_odata() as od: + return await od._list_columns(table, select=select, filter=filter) + + # ------------------------------------------------- list_relationships + + async def list_relationships( + self, + *, + filter: Optional[str] = None, + select: Optional[List[str]] = None, + ) -> List[Dict[str, Any]]: + """List all relationship definitions in the environment. + + :param filter: Optional OData ``$filter`` expression. For example, + ``"RelationshipType eq Microsoft.Dynamics.CRM.RelationshipType'OneToManyRelationship'"`` + returns only one-to-many relationships. + :type filter: :class:`str` or None + :param select: Optional list of property names to project via + ``$select``. Values are passed as-is (PascalCase). + :type select: list[str] or None + + :return: List of raw relationship metadata dictionaries. + :rtype: list[dict[str, typing.Any]] + + :raises ~PowerPlatform.Dataverse.core.errors.HttpError: + If the Web API request fails. + + Example:: + + # List all relationships + rels = await client.tables.list_relationships() + for rel in rels: + print(f"{rel['SchemaName']} ({rel.get('@odata.type')})") + + # Filter by type + one_to_many = await client.tables.list_relationships( + filter="RelationshipType eq Microsoft.Dynamics.CRM.RelationshipType'OneToManyRelationship'" + ) + + # Select specific properties + rels = await client.tables.list_relationships( + select=["SchemaName", "ReferencedEntity", "ReferencingEntity"] + ) + """ + async with self._client._scoped_odata() as od: + return await od._list_relationships(filter=filter, select=select) + + # --------------------------------------------- list_table_relationships + + async def list_table_relationships( + self, + table: str, + *, + filter: Optional[str] = None, + select: Optional[List[str]] = None, + ) -> List[Dict[str, Any]]: + """List all relationships for a specific table. + + Combines one-to-many, many-to-one, and many-to-many relationships + for the given table by querying + ``EntityDefinitions({id})/OneToManyRelationships``, + ``EntityDefinitions({id})/ManyToOneRelationships``, and + ``EntityDefinitions({id})/ManyToManyRelationships``. + + :param table: Schema name of the table (e.g. ``"account"``). + :type table: :class:`str` + :param filter: Optional OData ``$filter`` expression applied to each + sub-request. + :type filter: :class:`str` or None + :param select: Optional list of property names to project via + ``$select``. Values are passed as-is (PascalCase). + :type select: list[str] or None + + :return: Combined list of one-to-many, many-to-one, and many-to-many + relationship metadata dictionaries. + :rtype: list[dict[str, typing.Any]] + + :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: + If the table is not found. + :raises ~PowerPlatform.Dataverse.core.errors.HttpError: + If the Web API request fails. + + Example:: + + # List all relationships for the account table + rels = await client.tables.list_table_relationships("account") + for rel in rels: + print(f"{rel['SchemaName']} -> {rel.get('@odata.type')}") + """ + async with self._client._scoped_odata() as od: + return await od._list_table_relationships(table, filter=filter, select=select) diff --git a/src/PowerPlatform/Dataverse/client.py b/src/PowerPlatform/Dataverse/client.py index 12ceaaac..c9a1364f 100644 --- a/src/PowerPlatform/Dataverse/client.py +++ b/src/PowerPlatform/Dataverse/client.py @@ -105,7 +105,7 @@ def __init__( ) -> None: if config is not None and context is not None: raise ValueError( - "Cannot specify both 'config' and 'context'. Pass operation_context via DataverseConfig instead." + "Cannot specify both 'config' and 'context'. " "Pass operation_context via DataverseConfig instead." ) self.auth = _AuthManager(credential) self._base_url = (base_url or "").rstrip("/") diff --git a/src/PowerPlatform/Dataverse/models/record.py b/src/PowerPlatform/Dataverse/models/record.py index da65e7a5..e81f412c 100644 --- a/src/PowerPlatform/Dataverse/models/record.py +++ b/src/PowerPlatform/Dataverse/models/record.py @@ -6,7 +6,7 @@ from __future__ import annotations from dataclasses import dataclass, field -from typing import Any, Dict, Iterator, KeysView, List, Optional, Union, ValuesView, ItemsView +from typing import Any, Dict, Iterator, KeysView, List, Optional, ValuesView, ItemsView __all__ = ["Record", "QueryResult"] @@ -141,7 +141,7 @@ def __bool__(self) -> bool: def __repr__(self) -> str: return f"QueryResult({len(self.records)} records)" - def __getitem__(self, index: Union[int, slice]) -> Union[Record, "QueryResult"]: + def __getitem__(self, index): result = self.records[index] return QueryResult(result) if isinstance(index, slice) else result diff --git a/src/PowerPlatform/Dataverse/operations/query.py b/src/PowerPlatform/Dataverse/operations/query.py index 1f3c8ef2..c927bdf0 100644 --- a/src/PowerPlatform/Dataverse/operations/query.py +++ b/src/PowerPlatform/Dataverse/operations/query.py @@ -33,14 +33,12 @@ class QueryOperations: Example:: - from PowerPlatform.Dataverse.models.filters import col - client = DataverseClient(base_url, credential) # Fluent query builder (recommended) for record in (client.query.builder("account") .select("name", "revenue") - .where(col("statecode") == 0) + .filter_eq("statecode", 0) .order_by("revenue", descending=True) .top(100) .execute()): @@ -72,12 +70,10 @@ def builder(self, table: str) -> QueryBuilder: Example: Build and execute a query fluently:: - from PowerPlatform.Dataverse.models.filters import col - for record in (client.query.builder("account") .select("name", "revenue") - .where(col("statecode") == 0) - .where(col("revenue") > 1_000_000) + .filter_eq("statecode", 0) + .filter_gt("revenue", 1000000) .order_by("revenue", descending=True) .top(100) .page_size(50) @@ -86,11 +82,11 @@ def builder(self, table: str) -> QueryBuilder: With composable expression tree:: - from PowerPlatform.Dataverse.models.filters import col + from PowerPlatform.Dataverse.models.filters import eq, gt for record in (client.query.builder("account") - .where((col("statecode") == 0) | (col("statecode") == 1)) - .where(col("revenue") > 100_000) + .where((eq("statecode", 0) | eq("statecode", 1)) + & gt("revenue", 100000)) .execute()): print(record["name"]) """ diff --git a/tests/unit/aio/__init__.py b/tests/unit/aio/__init__.py new file mode 100644 index 00000000..9a045456 --- /dev/null +++ b/tests/unit/aio/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. diff --git a/tests/unit/aio/conftest.py b/tests/unit/aio/conftest.py new file mode 100644 index 00000000..ab29d7c9 --- /dev/null +++ b/tests/unit/aio/conftest.py @@ -0,0 +1,35 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +"""Shared fixtures for async unit tests.""" + +from contextlib import asynccontextmanager +from unittest.mock import AsyncMock, MagicMock + +import pytest +from azure.core.credentials_async import AsyncTokenCredential + +from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient + + +@pytest.fixture +def mock_od() -> AsyncMock: + """AsyncMock representing the low-level _AsyncODataClient.""" + od = AsyncMock() + # _call_scope() is a sync context manager; MagicMock supports __enter__/__exit__ + od._call_scope.return_value = MagicMock() + return od + + +@pytest.fixture +def async_client(mock_od: AsyncMock) -> AsyncDataverseClient: + """AsyncDataverseClient with _scoped_odata patched to yield mock_od.""" + cred = MagicMock(spec=AsyncTokenCredential) + client = AsyncDataverseClient("https://example.crm.dynamics.com", cred) + + @asynccontextmanager + async def _fake_scoped_odata(): + yield mock_od + + client._scoped_odata = _fake_scoped_odata + return client diff --git a/tests/unit/aio/core/__init__.py b/tests/unit/aio/core/__init__.py new file mode 100644 index 00000000..9a045456 --- /dev/null +++ b/tests/unit/aio/core/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. diff --git a/tests/unit/aio/core/test_async_auth.py b/tests/unit/aio/core/test_async_auth.py new file mode 100644 index 00000000..6b425826 --- /dev/null +++ b/tests/unit/aio/core/test_async_auth.py @@ -0,0 +1,49 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +import pytest +from unittest.mock import AsyncMock, MagicMock + +from azure.core.credentials_async import AsyncTokenCredential + +from PowerPlatform.Dataverse.aio.core._async_auth import _AsyncAuthManager +from PowerPlatform.Dataverse.core._auth import _TokenPair + + +class TestAsyncAuthManager: + """Tests for _AsyncAuthManager credential validation and token acquisition.""" + + def test_non_async_token_credential_raises(self): + """_AsyncAuthManager raises TypeError when credential does not implement AsyncTokenCredential.""" + with pytest.raises(TypeError) as exc_info: + _AsyncAuthManager("not-a-credential") + assert "AsyncTokenCredential" in str(exc_info.value) + + def test_valid_credential_accepted(self): + """_AsyncAuthManager accepts a valid AsyncTokenCredential.""" + mock_cred = MagicMock(spec=AsyncTokenCredential) + manager = _AsyncAuthManager(mock_cred) + assert manager.credential is mock_cred + + async def test_acquire_token_returns_token_pair(self): + """_acquire_token calls get_token and returns a _TokenPair with scope and token.""" + mock_cred = MagicMock(spec=AsyncTokenCredential) + mock_cred.get_token = AsyncMock(return_value=MagicMock(token="my-access-token")) + + manager = _AsyncAuthManager(mock_cred) + result = await manager._acquire_token("https://org.crm.dynamics.com/.default") + + mock_cred.get_token.assert_called_once_with("https://org.crm.dynamics.com/.default") + assert isinstance(result, _TokenPair) + assert result.resource == "https://org.crm.dynamics.com/.default" + assert result.access_token == "my-access-token" + + async def test_acquire_token_different_scope(self): + """_acquire_token passes the scope string through to get_token.""" + mock_cred = MagicMock(spec=AsyncTokenCredential) + mock_cred.get_token = AsyncMock(return_value=MagicMock(token="tok")) + + manager = _AsyncAuthManager(mock_cred) + await manager._acquire_token("https://example.crm10.dynamics.com/.default") + + mock_cred.get_token.assert_called_once_with("https://example.crm10.dynamics.com/.default") diff --git a/tests/unit/aio/core/test_async_http.py b/tests/unit/aio/core/test_async_http.py new file mode 100644 index 00000000..43708aba --- /dev/null +++ b/tests/unit/aio/core/test_async_http.py @@ -0,0 +1,286 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +import pytest +from unittest.mock import AsyncMock, MagicMock, call, patch + +import aiohttp + +from PowerPlatform.Dataverse.aio.core._async_http import _AsyncHttpClient, _AsyncResponse + + +def _make_resp(status: int = 200) -> MagicMock: + """Return a mock aiohttp.ClientResponse.""" + resp = MagicMock() + resp.status = status + resp.headers = {} + resp.read = AsyncMock(return_value=b"") + return resp + + +def _make_cm(resp=None, exc=None) -> MagicMock: + """Return an async context manager mock. + + If exc is given, __aenter__ raises it. Otherwise it returns resp. + """ + cm = MagicMock() + if exc is not None: + cm.__aenter__ = AsyncMock(side_effect=exc) + else: + cm.__aenter__ = AsyncMock(return_value=resp) + cm.__aexit__ = AsyncMock(return_value=False) + return cm + + +def _make_session(status: int = 200) -> MagicMock: + """Return a mock aiohttp.ClientSession whose request() is an async context manager.""" + session = MagicMock(spec=aiohttp.ClientSession) + session.request = MagicMock(return_value=_make_cm(_make_resp(status))) + return session + + +class TestAsyncHttpClientTimeout: + """Tests for automatic timeout selection in _AsyncHttpClient._request.""" + + async def test_get_uses_10s_default_timeout(self): + """GET requests use 10 s default when no timeout is specified.""" + session = _make_session() + client = _AsyncHttpClient(retries=1, session=session) + await client._request("get", "https://example.com/data") + _, kwargs = session.request.call_args + assert isinstance(kwargs["timeout"], aiohttp.ClientTimeout) + assert kwargs["timeout"].total == 10 + + async def test_post_uses_120s_default_timeout(self): + """POST requests use 120 s default when no timeout is specified.""" + session = _make_session() + client = _AsyncHttpClient(retries=1, session=session) + await client._request("post", "https://example.com/data") + _, kwargs = session.request.call_args + assert kwargs["timeout"].total == 120 + + async def test_delete_uses_120s_default_timeout(self): + """DELETE requests use 120 s default when no timeout is specified.""" + session = _make_session() + client = _AsyncHttpClient(retries=1, session=session) + await client._request("delete", "https://example.com/data") + _, kwargs = session.request.call_args + assert kwargs["timeout"].total == 120 + + async def test_put_uses_10s_default_timeout(self): + """PUT requests use 10 s default (only POST/DELETE get 120 s).""" + session = _make_session() + client = _AsyncHttpClient(retries=1, session=session) + await client._request("put", "https://example.com/data") + _, kwargs = session.request.call_args + assert kwargs["timeout"].total == 10 + + async def test_patch_uses_10s_default_timeout(self): + """PATCH requests use 10 s default (only POST/DELETE get 120 s).""" + session = _make_session() + client = _AsyncHttpClient(retries=1, session=session) + await client._request("patch", "https://example.com/data") + _, kwargs = session.request.call_args + assert kwargs["timeout"].total == 10 + + async def test_custom_client_timeout_overrides_method_default(self): + """Explicit default_timeout on the client overrides per-method defaults.""" + session = _make_session() + client = _AsyncHttpClient(retries=1, timeout=30.0, session=session) + await client._request("get", "https://example.com/data") + _, kwargs = session.request.call_args + assert kwargs["timeout"].total == 30.0 + + async def test_explicit_timeout_kwarg_takes_precedence(self): + """If timeout is already in kwargs it is passed through unchanged.""" + session = _make_session() + client = _AsyncHttpClient(retries=1, timeout=30.0, session=session) + custom_timeout = aiohttp.ClientTimeout(total=5) + await client._request("get", "https://example.com/data", timeout=custom_timeout) + _, kwargs = session.request.call_args + assert kwargs["timeout"] is custom_timeout + + +class TestAsyncHttpClientNoSession: + """Tests for RuntimeError when no session is provided.""" + + async def test_raises_runtime_error_without_session(self): + """_request raises RuntimeError if no session has been set.""" + client = _AsyncHttpClient(retries=1) + with pytest.raises(RuntimeError, match="No aiohttp.ClientSession"): + await client._request("get", "https://example.com") + + +class TestAsyncHttpClientRetry: + """Tests for retry behavior on aiohttp.ClientError.""" + + async def test_retries_on_client_error_and_succeeds(self): + """Retries after a ClientError and returns response on second attempt.""" + session = MagicMock(spec=aiohttp.ClientSession) + good_resp = _make_resp(200) + session.request = MagicMock( + side_effect=[ + _make_cm(exc=aiohttp.ClientConnectionError("timeout")), + _make_cm(good_resp), + ] + ) + client = _AsyncHttpClient(retries=2, backoff=0, session=session) + with patch("asyncio.sleep", new_callable=AsyncMock): + result = await client._request("get", "https://example.com/data") + + assert session.request.call_count == 2 + assert isinstance(result, _AsyncResponse) + assert result.status == 200 + + async def test_raises_after_all_retries_exhausted(self): + """Raises ClientError after all retry attempts fail.""" + session = MagicMock(spec=aiohttp.ClientSession) + session.request = MagicMock(return_value=_make_cm(exc=aiohttp.ClientConnectionError("timeout"))) + client = _AsyncHttpClient(retries=3, backoff=0, session=session) + with patch("asyncio.sleep", new_callable=AsyncMock): + with pytest.raises(aiohttp.ClientError): + await client._request("get", "https://example.com/data") + + async def test_backoff_delay_between_retries(self): + """Sleeps with exponential backoff between retry attempts.""" + session = MagicMock(spec=aiohttp.ClientSession) + good_resp = _make_resp(200) + session.request = MagicMock( + side_effect=[ + _make_cm(exc=aiohttp.ClientConnectionError()), + _make_cm(exc=aiohttp.ClientConnectionError()), + _make_cm(good_resp), + ] + ) + client = _AsyncHttpClient(retries=3, backoff=1.0, session=session) + with patch("asyncio.sleep", new_callable=AsyncMock) as mock_sleep: + await client._request("get", "https://example.com/data") + # First retry: 1.0 * 2^0 = 1.0; second retry: 1.0 * 2^1 = 2.0 + mock_sleep.assert_has_calls([call(1.0), call(2.0)]) + + async def test_no_retry_on_success(self): + """Single successful response does not trigger retries.""" + session = _make_session(200) + client = _AsyncHttpClient(retries=5, backoff=0, session=session) + await client._request("get", "https://example.com/data") + assert session.request.call_count == 1 + + async def test_retries_on_timeout_error(self): + """Retries on asyncio.TimeoutError (not a subclass of aiohttp.ClientError).""" + import asyncio + + session = MagicMock(spec=aiohttp.ClientSession) + good_resp = _make_resp(200) + session.request = MagicMock( + side_effect=[ + _make_cm(exc=asyncio.TimeoutError()), + _make_cm(good_resp), + ] + ) + client = _AsyncHttpClient(retries=2, backoff=0, session=session) + with patch("asyncio.sleep", new_callable=AsyncMock): + result = await client._request("get", "https://example.com/data") + + assert session.request.call_count == 2 + assert isinstance(result, _AsyncResponse) + assert result.status == 200 + + +class TestAsyncHttpClientClose: + """Tests for _AsyncHttpClient.close().""" + + async def test_close_closes_session(self): + """close() closes the session and sets _session to None.""" + session = MagicMock(spec=aiohttp.ClientSession) + session.close = AsyncMock() + client = _AsyncHttpClient(retries=1, session=session) + await client.close() + session.close.assert_called_once() + assert client._session is None + + async def test_close_without_session_is_safe(self): + """close() is safe to call when no session was set.""" + client = _AsyncHttpClient(retries=1) + await client.close() # should not raise + + +class TestAsyncHttpClientLogger: + """Tests for request logging via _HttpLogger integration.""" + + async def test_request_logged_when_logger_set(self): + """Outbound request is logged once when a logger is attached.""" + session = _make_session() + mock_logger = MagicMock() + mock_logger.body_logging_enabled = False + client = _AsyncHttpClient(retries=1, session=session, logger=mock_logger) + await client._request("get", "https://example.com/data") + mock_logger.log_request.assert_called_once() + + async def test_response_logged_when_logger_set(self): + """HTTP response is logged when a logger is attached.""" + session = _make_session() + mock_logger = MagicMock() + mock_logger.body_logging_enabled = False + client = _AsyncHttpClient(retries=1, session=session, logger=mock_logger) + await client._request("get", "https://example.com/data") + mock_logger.log_response.assert_called_once() + + async def test_error_logged_on_retry(self): + """Transport errors are logged before each retry.""" + session = MagicMock(spec=aiohttp.ClientSession) + good_resp = _make_resp(200) + session.request = MagicMock( + side_effect=[ + _make_cm(exc=aiohttp.ClientConnectionError()), + _make_cm(good_resp), + ] + ) + mock_logger = MagicMock() + mock_logger.body_logging_enabled = False + client = _AsyncHttpClient(retries=2, backoff=0, session=session, logger=mock_logger) + with patch("asyncio.sleep", new_callable=AsyncMock): + await client._request("get", "https://example.com/data") + mock_logger.log_error.assert_called_once() + + async def test_request_body_logged_from_json_kwarg(self): + """json= kwarg body is extracted and passed to log_request.""" + session = _make_session() + mock_logger = MagicMock() + mock_logger.body_logging_enabled = False + client = _AsyncHttpClient(retries=1, session=session, logger=mock_logger) + await client._request("post", "https://example.com/data", json={"key": "value"}) + _, log_kwargs = mock_logger.log_request.call_args + assert log_kwargs["body"] == {"key": "value"} + + async def test_request_body_logged_from_data_kwarg(self): + """data= kwarg body is extracted when json= is absent.""" + session = _make_session() + mock_logger = MagicMock() + mock_logger.body_logging_enabled = False + client = _AsyncHttpClient(retries=1, session=session, logger=mock_logger) + await client._request("post", "https://example.com/data", data=b"raw bytes") + _, log_kwargs = mock_logger.log_request.call_args + assert log_kwargs["body"] == b"raw bytes" + + async def test_response_body_decoded_when_body_logging_enabled(self): + """When body_logging_enabled=True, response bytes are decoded and passed to log_response.""" + session = _make_session() + session.request.return_value.__aenter__.return_value.read = AsyncMock(return_value=b'{"ok": true}') + mock_logger = MagicMock() + mock_logger.body_logging_enabled = True + client = _AsyncHttpClient(retries=1, session=session, logger=mock_logger) + await client._request("get", "https://example.com/data") + _, log_kwargs = mock_logger.log_response.call_args + assert log_kwargs["body"] == '{"ok": true}' + + async def test_response_body_invalid_bytes_replaced_in_logging(self): + """Invalid UTF-8 bytes in response body are replaced (not raised) when body logging is enabled.""" + session = _make_session() + session.request.return_value.__aenter__.return_value.read = AsyncMock(return_value=b"\xff\xfe invalid") + mock_logger = MagicMock() + mock_logger.body_logging_enabled = True + client = _AsyncHttpClient(retries=1, session=session, logger=mock_logger) + await client._request("get", "https://example.com/data") + _, log_kwargs = mock_logger.log_response.call_args + # errors="replace" means invalid bytes become replacement chars — body is a str, never raises + assert isinstance(log_kwargs["body"], str) diff --git a/tests/unit/aio/data/__init__.py b/tests/unit/aio/data/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/unit/aio/data/test_async_batch_internal.py b/tests/unit/aio/data/test_async_batch_internal.py new file mode 100644 index 00000000..1a24deed --- /dev/null +++ b/tests/unit/aio/data/test_async_batch_internal.py @@ -0,0 +1,839 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +"""Unit tests for _AsyncBatchClient internals.""" + +from unittest.mock import AsyncMock, MagicMock, patch + +import pytest + +from PowerPlatform.Dataverse.aio.data._async_batch import _AsyncBatchClient +from PowerPlatform.Dataverse.aio.core._async_http import _AsyncResponse +from PowerPlatform.Dataverse.core.errors import MetadataError, ValidationError +from PowerPlatform.Dataverse.data._batch_base import ( + _RecordCreate, + _RecordDelete, + _RecordGet, + _RecordList, + _RecordUpdate, + _RecordUpsert, + _TableAddColumns, + _TableCreate, + _TableDelete, + _TableGet, + _TableList, + _TableCreateOneToMany, + _TableCreateManyToMany, + _TableDeleteRelationship, + _TableGetRelationship, + _TableCreateLookupField, + _TableRemoveColumns, + _QuerySql, + _ChangeSet, + _MAX_BATCH_SIZE, +) +from PowerPlatform.Dataverse.models.upsert import UpsertItem + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + + +def _make_batch_client(): + """Return _AsyncBatchClient with a fully-mocked _AsyncODataClient. + + All _build_* methods are pre-mocked so resolver tests can run without + any real OData or HTTP logic. Sync _build_* methods use MagicMock; + async ones use AsyncMock. + """ + od = AsyncMock() + od.api = "https://example.crm.dynamics.com/api/data/v9.2" + od._entity_set_from_schema_name = AsyncMock(return_value="accounts") + od._primary_id_attr = AsyncMock(return_value="accountid") + od._get_entity_by_table_schema_name = AsyncMock( + return_value={"MetadataId": "meta-1", "LogicalName": "account", "SchemaName": "Account"} + ) + od._get_attribute_metadata = AsyncMock(return_value={"MetadataId": "attr-1", "LogicalName": "new_notes"}) + od._build_create = AsyncMock( + return_value=MagicMock(method="POST", url="https://x/accounts", body="{}", headers=None, content_id=None) + ) + od._build_create_multiple = AsyncMock( + return_value=MagicMock( + method="POST", url="https://x/accounts/CreateMultiple", body="{}", headers=None, content_id=None + ) + ) + od._build_update = AsyncMock( + return_value=MagicMock( + method="PATCH", url="https://x/accounts(g)", body="{}", headers={"If-Match": "*"}, content_id=None + ) + ) + od._build_update_multiple = AsyncMock( + return_value=MagicMock( + method="POST", url="https://x/accounts/UpdateMultiple", body="{}", headers=None, content_id=None + ) + ) + od._build_delete = AsyncMock( + return_value=MagicMock( + method="DELETE", url="https://x/accounts(g)", body=None, headers={"If-Match": "*"}, content_id=None + ) + ) + od._build_delete_multiple = AsyncMock( + return_value=MagicMock(method="POST", url="https://x/BulkDelete", body="{}", headers=None, content_id=None) + ) + od._build_get = AsyncMock( + return_value=MagicMock(method="GET", url="https://x/accounts(g)", body=None, headers=None, content_id=None) + ) + od._build_upsert = AsyncMock( + return_value=MagicMock(method="PATCH", url="https://x/accounts(k)", body="{}", headers=None, content_id=None) + ) + od._build_upsert_multiple = AsyncMock( + return_value=MagicMock( + method="POST", url="https://x/accounts/UpsertMultiple", body="{}", headers=None, content_id=None + ) + ) + od._build_list = AsyncMock( + return_value=MagicMock(method="GET", url="https://x/accounts", body=None, headers=None, content_id=None) + ) + od._build_sql = AsyncMock( + return_value=MagicMock(method="GET", url="https://x/accounts?sql=...", body=None, headers=None, content_id=None) + ) + od._build_delete_entity = MagicMock( + return_value=MagicMock( + method="DELETE", url="https://x/EntityDefinitions(m)", body=None, headers=None, content_id=None + ) + ) + od._build_create_column = MagicMock( + return_value=MagicMock( + method="POST", url="https://x/EntityDefinitions(m)/Attributes", body="{}", headers=None, content_id=None + ) + ) + od._build_delete_column = MagicMock( + return_value=MagicMock( + method="DELETE", + url="https://x/EntityDefinitions(m)/Attributes(a)", + body=None, + headers=None, + content_id=None, + ) + ) + # Sync _build_* for pure-logic table intents inherited from _BatchBase + _raw = lambda method, url: MagicMock(method=method, url=url, body=None, headers=None, content_id=None) + od._build_create_entity = MagicMock(return_value=_raw("POST", "https://x/EntityDefinitions")) + od._build_get_entity = MagicMock(return_value=_raw("GET", "https://x/EntityDefinitions(m)")) + od._build_list_entities = MagicMock(return_value=_raw("GET", "https://x/EntityDefinitions")) + od._build_create_relationship = MagicMock(return_value=_raw("POST", "https://x/RelationshipDefinitions")) + od._build_delete_relationship = MagicMock(return_value=_raw("DELETE", "https://x/RelationshipDefinitions(r)")) + od._build_get_relationship = MagicMock(return_value=_raw("GET", "https://x/RelationshipDefinitions(r)")) + _mock_lookup = MagicMock() + _mock_lookup.to_dict.return_value = {} + _mock_rel = MagicMock() + _mock_rel.to_dict.return_value = {} + od._build_lookup_field_models = MagicMock(return_value=(_mock_lookup, _mock_rel)) + return _AsyncBatchClient(od), od + + +def _batch_resp(status=200, text="", json_payload=None): + """Create a mock _AsyncResponse-compatible response for the batch execute() path.""" + r = MagicMock() + r.status = status + r.status_code = status + r.headers = {"Content-Type": "application/json"} + r.text = text + r.json = MagicMock(return_value=json_payload or {}) + return r + + +# --------------------------------------------------------------------------- +# execute() +# --------------------------------------------------------------------------- + + +class TestExecute: + """Tests for execute(), the public entry point that dispatches the full batch request.""" + + async def test_empty_items_returns_empty_result(self): + """An empty items list short-circuits and returns an empty BatchResult without HTTP.""" + client, _ = _make_batch_client() + result = await client.execute([]) + assert result is not None + + async def test_executes_single_record_create(self): + """A single RecordCreate item causes exactly one _request call.""" + client, od = _make_batch_client() + from PowerPlatform.Dataverse.models.batch import BatchResult + + resp_mock = _batch_resp(status=200) + od._request = AsyncMock(return_value=resp_mock) + item = _RecordCreate(table="account", data={"name": "X"}) + with patch.object(client, "_parse_batch_response", return_value=BatchResult()): + await client.execute([item]) + od._request.assert_called_once() + + async def test_executes_with_continue_on_error(self): + """The odata.continue-on-error preference is injected when continue_on_error=True.""" + client, od = _make_batch_client() + from PowerPlatform.Dataverse.models.batch import BatchResult + + resp_mock = _batch_resp(status=200) + od._request = AsyncMock(return_value=resp_mock) + item = _RecordCreate(table="account", data={"name": "X"}) + with patch.object(client, "_parse_batch_response", return_value=BatchResult()): + await client.execute([item], continue_on_error=True) + call_kwargs = od._request.call_args.kwargs + headers = call_kwargs.get("headers", {}) + assert "odata.continue-on-error" in headers.get("Prefer", "") + + +# --------------------------------------------------------------------------- +# _resolve_record_create() +# --------------------------------------------------------------------------- + + +class TestResolveRecordCreate: + """Tests for _resolve_record_create() intent-to-request translation.""" + + async def test_single_dict_returns_one_request(self): + """A dict payload produces a single _build_create request.""" + client, od = _make_batch_client() + op = _RecordCreate(table="account", data={"name": "X"}) + result = await client._resolve_record_create(op) + assert len(result) == 1 + od._build_create.assert_called_once() + + async def test_list_returns_one_create_multiple_request(self): + """A list payload produces a single _build_create_multiple request.""" + client, od = _make_batch_client() + op = _RecordCreate(table="account", data=[{"name": "X"}, {"name": "Y"}]) + result = await client._resolve_record_create(op) + assert len(result) == 1 + od._build_create_multiple.assert_called_once() + + +# --------------------------------------------------------------------------- +# _resolve_record_update() +# --------------------------------------------------------------------------- + + +class TestResolveRecordUpdate: + """Tests for _resolve_record_update() intent-to-request translation.""" + + async def test_single_id_string_returns_one_patch(self): + """A single string ID with a dict of changes produces one _build_update request.""" + client, od = _make_batch_client() + op = _RecordUpdate(table="account", ids="guid-1", changes={"name": "X"}) + result = await client._resolve_record_update(op) + assert len(result) == 1 + od._build_update.assert_called_once() + + async def test_single_id_non_dict_changes_raises(self): + """TypeError is raised when changes is not a dict for a single-ID update.""" + client, od = _make_batch_client() + op = _RecordUpdate(table="account", ids="guid-1", changes=["invalid"]) + with pytest.raises(TypeError): + await client._resolve_record_update(op) + + async def test_list_ids_returns_update_multiple(self): + """A list of IDs produces a single _build_update_multiple request.""" + client, od = _make_batch_client() + op = _RecordUpdate(table="account", ids=["id-1", "id-2"], changes={"name": "X"}) + result = await client._resolve_record_update(op) + assert len(result) == 1 + od._build_update_multiple.assert_called_once() + + +# --------------------------------------------------------------------------- +# _resolve_record_delete() +# --------------------------------------------------------------------------- + + +class TestResolveRecordDelete: + """Tests for _resolve_record_delete() intent-to-request translation.""" + + async def test_single_id_string(self): + """A single string ID produces one _build_delete request.""" + client, od = _make_batch_client() + op = _RecordDelete(table="account", ids="guid-1") + result = await client._resolve_record_delete(op) + assert len(result) == 1 + od._build_delete.assert_called_once() + + async def test_list_ids_bulk_delete(self): + """A list of IDs with use_bulk_delete=True produces one _build_delete_multiple request.""" + client, od = _make_batch_client() + op = _RecordDelete(table="account", ids=["id-1", "id-2"], use_bulk_delete=True) + result = await client._resolve_record_delete(op) + assert len(result) == 1 + od._build_delete_multiple.assert_called_once() + + async def test_list_ids_sequential_delete(self): + """A list of IDs with use_bulk_delete=False produces one _build_delete per ID.""" + client, od = _make_batch_client() + op = _RecordDelete(table="account", ids=["id-1", "id-2"], use_bulk_delete=False) + result = await client._resolve_record_delete(op) + assert len(result) == 2 + assert od._build_delete.call_count == 2 + + async def test_empty_ids_list_returns_empty(self): + """An empty IDs list produces no requests.""" + client, od = _make_batch_client() + op = _RecordDelete(table="account", ids=[]) + result = await client._resolve_record_delete(op) + assert result == [] + + +# --------------------------------------------------------------------------- +# _resolve_record_get() +# --------------------------------------------------------------------------- + + +class TestResolveRecordGet: + """Tests for _resolve_record_get() intent-to-request translation.""" + + async def test_single_get_request(self): + """A RecordGet op produces one _build_get request with the correct arguments.""" + client, od = _make_batch_client() + op = _RecordGet(table="account", record_id="guid-1", select=["name"]) + result = await client._resolve_record_get(op) + assert len(result) == 1 + od._build_get.assert_called_once_with( + "account", "guid-1", select=["name"], expand=None, include_annotations=None + ) + + async def test_passes_expand_to_build_get(self): + """expand= is forwarded from _RecordGet to _build_get.""" + client, od = _make_batch_client() + op = _RecordGet(table="account", record_id="guid-1", expand=["primarycontactid"]) + await client._resolve_record_get(op) + od._build_get.assert_called_once_with( + "account", "guid-1", select=None, expand=["primarycontactid"], include_annotations=None + ) + + async def test_passes_include_annotations_to_build_get(self): + """include_annotations= is forwarded from _RecordGet to _build_get.""" + client, od = _make_batch_client() + annotation = "OData.Community.Display.V1.FormattedValue" + op = _RecordGet(table="account", record_id="guid-1", include_annotations=annotation) + await client._resolve_record_get(op) + od._build_get.assert_called_once_with( + "account", "guid-1", select=None, expand=None, include_annotations=annotation + ) + + async def test_passes_all_params_to_build_get(self): + """All _RecordGet fields are forwarded together to _build_get.""" + client, od = _make_batch_client() + annotation = "OData.Community.Display.V1.FormattedValue" + op = _RecordGet( + table="account", + record_id="guid-1", + select=["name"], + expand=["primarycontactid"], + include_annotations=annotation, + ) + result = await client._resolve_record_get(op) + assert len(result) == 1 + od._build_get.assert_called_once_with( + "account", "guid-1", select=["name"], expand=["primarycontactid"], include_annotations=annotation + ) + + +# --------------------------------------------------------------------------- +# _resolve_record_list() +# --------------------------------------------------------------------------- + + +class TestResolveRecordList: + """Tests for _resolve_record_list() intent-to-request translation.""" + + async def test_produces_one_request(self): + """A _RecordList op produces exactly one _build_list request.""" + client, od = _make_batch_client() + op = _RecordList(table="account") + result = await client._resolve_record_list(op) + assert len(result) == 1 + od._build_list.assert_called_once() + + async def test_passes_table_to_build_list(self): + """The table name is forwarded to _build_list as first positional arg.""" + client, od = _make_batch_client() + op = _RecordList(table="contact") + await client._resolve_record_list(op) + call_args = od._build_list.call_args + assert call_args[0][0] == "contact" + + async def test_passes_filter(self): + """filter= is forwarded from _RecordList to _build_list.""" + client, od = _make_batch_client() + op = _RecordList(table="account", filter="statecode eq 0") + await client._resolve_record_list(op) + assert od._build_list.call_args[1]["filter"] == "statecode eq 0" + + async def test_passes_select(self): + """select= is forwarded from _RecordList to _build_list.""" + client, od = _make_batch_client() + op = _RecordList(table="account", select=["name", "revenue"]) + await client._resolve_record_list(op) + assert od._build_list.call_args[1]["select"] == ["name", "revenue"] + + async def test_passes_top(self): + """top= is forwarded from _RecordList to _build_list.""" + client, od = _make_batch_client() + op = _RecordList(table="account", top=50) + await client._resolve_record_list(op) + assert od._build_list.call_args[1]["top"] == 50 + + async def test_passes_orderby(self): + """orderby= is forwarded from _RecordList to _build_list.""" + client, od = _make_batch_client() + op = _RecordList(table="account", orderby=["name asc"]) + await client._resolve_record_list(op) + assert od._build_list.call_args[1]["orderby"] == ["name asc"] + + async def test_passes_expand(self): + """expand= is forwarded from _RecordList to _build_list.""" + client, od = _make_batch_client() + op = _RecordList(table="account", expand=["primarycontactid"]) + await client._resolve_record_list(op) + assert od._build_list.call_args[1]["expand"] == ["primarycontactid"] + + async def test_passes_page_size(self): + """page_size= is forwarded from _RecordList to _build_list.""" + client, od = _make_batch_client() + op = _RecordList(table="account", page_size=200) + await client._resolve_record_list(op) + assert od._build_list.call_args[1]["page_size"] == 200 + + async def test_passes_count(self): + """count=True is forwarded from _RecordList to _build_list.""" + client, od = _make_batch_client() + op = _RecordList(table="account", count=True) + await client._resolve_record_list(op) + assert od._build_list.call_args[1]["count"] is True + + async def test_passes_include_annotations(self): + """include_annotations= is forwarded from _RecordList to _build_list.""" + client, od = _make_batch_client() + annotation = "OData.Community.Display.V1.FormattedValue" + op = _RecordList(table="account", include_annotations=annotation) + await client._resolve_record_list(op) + assert od._build_list.call_args[1]["include_annotations"] == annotation + + async def test_resolve_item_dispatch(self): + """_resolve_item dispatches _RecordList correctly.""" + client, od = _make_batch_client() + op = _RecordList(table="account", filter="statecode eq 0") + result = await client._resolve_item(op) + assert len(result) == 1 + od._build_list.assert_called_once() + + +# --------------------------------------------------------------------------- +# _resolve_record_upsert() +# --------------------------------------------------------------------------- + + +class TestResolveRecordUpsert: + """Tests for _resolve_record_upsert() intent-to-request translation.""" + + async def test_single_item_calls_build_upsert(self): + """A single UpsertItem produces one _build_upsert request.""" + client, od = _make_batch_client() + item = UpsertItem(alternate_key={"accountnumber": "A"}, record={"name": "X"}) + op = _RecordUpsert(table="account", items=[item]) + result = await client._resolve_record_upsert(op) + assert len(result) == 1 + od._build_upsert.assert_called_once() + + async def test_multiple_items_calls_build_upsert_multiple(self): + """Multiple UpsertItems produce a single _build_upsert_multiple request.""" + client, od = _make_batch_client() + items = [ + UpsertItem(alternate_key={"accountnumber": "A"}, record={"name": "X"}), + UpsertItem(alternate_key={"accountnumber": "B"}, record={"name": "Y"}), + ] + op = _RecordUpsert(table="account", items=items) + result = await client._resolve_record_upsert(op) + assert len(result) == 1 + od._build_upsert_multiple.assert_called_once() + + +# --------------------------------------------------------------------------- +# _resolve_table_delete() +# --------------------------------------------------------------------------- + + +class TestResolveTableDelete: + """Tests for _resolve_table_delete() intent-to-request translation.""" + + async def test_resolves_to_delete_request(self): + """A TableDelete op resolves to a _build_delete_entity call using the table's MetadataId.""" + client, od = _make_batch_client() + op = _TableDelete(table="account") + result = await client._resolve_table_delete(op) + assert len(result) == 1 + od._build_delete_entity.assert_called_once_with("meta-1") + + async def test_table_not_found_raises(self): + """MetadataError is raised when the target table does not exist in metadata.""" + client, od = _make_batch_client() + od._get_entity_by_table_schema_name = AsyncMock(return_value=None) + op = _TableDelete(table="nonexistent") + with pytest.raises(MetadataError): + await client._resolve_table_delete(op) + + +# --------------------------------------------------------------------------- +# _resolve_table_add_columns() +# --------------------------------------------------------------------------- + + +class TestResolveTableAddColumns: + """Tests for _resolve_table_add_columns() intent-to-request translation.""" + + async def test_resolves_to_create_column_requests(self): + """Each column in the op produces one _build_create_column request.""" + client, od = _make_batch_client() + op = _TableAddColumns(table="account", columns={"col1": "string", "col2": "decimal"}) + result = await client._resolve_table_add_columns(op) + assert len(result) == 2 + assert od._build_create_column.call_count == 2 + + +# --------------------------------------------------------------------------- +# _resolve_table_remove_columns() +# --------------------------------------------------------------------------- + + +class TestResolveTableRemoveColumns: + """Tests for _resolve_table_remove_columns() intent-to-request translation.""" + + async def test_resolves_to_delete_column_requests(self): + """Each column in the list produces one _build_delete_column request.""" + client, od = _make_batch_client() + op = _TableRemoveColumns(table="account", columns=["col1", "col2"]) + result = await client._resolve_table_remove_columns(op) + assert len(result) == 2 + + async def test_string_column_name(self): + """A single column name supplied as a string produces one delete request.""" + client, od = _make_batch_client() + op = _TableRemoveColumns(table="account", columns="col1") + result = await client._resolve_table_remove_columns(op) + assert len(result) == 1 + + async def test_column_not_found_raises(self): + """MetadataError is raised when attribute metadata returns None for the column.""" + client, od = _make_batch_client() + od._get_attribute_metadata = AsyncMock(return_value=None) + op = _TableRemoveColumns(table="account", columns=["nonexistent"]) + with pytest.raises(MetadataError): + await client._resolve_table_remove_columns(op) + + async def test_attr_missing_metadata_id_raises(self): + """MetadataError is raised when attribute metadata lacks a MetadataId field.""" + client, od = _make_batch_client() + od._get_attribute_metadata = AsyncMock(return_value={"LogicalName": "col1"}) + op = _TableRemoveColumns(table="account", columns=["col1"]) + with pytest.raises(MetadataError): + await client._resolve_table_remove_columns(op) + + +# --------------------------------------------------------------------------- +# _resolve_query_sql() +# --------------------------------------------------------------------------- + + +class TestResolveQuerySql: + """Tests for _resolve_query_sql() intent-to-request translation.""" + + async def test_resolves_to_get_request(self): + """A QuerySql op produces one _build_sql request with the SQL statement.""" + client, od = _make_batch_client() + op = _QuerySql(sql="SELECT name FROM account") + result = await client._resolve_query_sql(op) + assert len(result) == 1 + od._build_sql.assert_called_once_with("SELECT name FROM account") + + +# --------------------------------------------------------------------------- +# _resolve_one() — changeset item must produce exactly 1 request +# --------------------------------------------------------------------------- + + +class TestResolveOne: + """Tests for _resolve_one(), which enforces the single-request contract for changeset items.""" + + async def test_single_request_returned(self): + """An op that resolves to exactly one request is returned without error.""" + client, od = _make_batch_client() + op = _RecordGet(table="account", record_id="guid-1") + req = await client._resolve_one(op) + assert req is not None + + async def test_multi_request_item_raises(self): + """ValidationError is raised when an op resolves to more than one request.""" + client, od = _make_batch_client() + # _RecordDelete with a list produces multiple requests (one per ID) + op = _RecordDelete(table="account", ids=["id-1", "id-2"], use_bulk_delete=False) + with pytest.raises(ValidationError, match="exactly one"): + await client._resolve_one(op) + + +# --------------------------------------------------------------------------- +# _resolve_all() — changeset handling +# --------------------------------------------------------------------------- + + +class TestResolveAll: + """Tests for _resolve_all(), which dispatches items and wraps changeset ops.""" + + async def test_empty_changeset_skipped(self): + """A ChangeSet with no operations is silently skipped without error.""" + client, od = _make_batch_client() + cs = _ChangeSet(operations=[]) + result = await client._resolve_all([cs]) + assert result == [] + + async def test_changeset_with_operations(self): + """A ChangeSet with one operation produces one _ChangeSetBatchItem in the result.""" + client, od = _make_batch_client() + op = _RecordCreate(table="account", data={"name": "X"}) + cs = _ChangeSet(operations=[op]) + result = await client._resolve_all([cs]) + assert len(result) == 1 + + async def test_unknown_item_type_raises(self): + """ValidationError is raised when an unrecognised item type is passed to _resolve_item.""" + client, od = _make_batch_client() + with pytest.raises(ValidationError, match="Unknown batch item type"): + await client._resolve_item("not-a-valid-type") + + +# --------------------------------------------------------------------------- +# _require_entity_metadata() +# --------------------------------------------------------------------------- + + +class TestRequireEntityMetadata: + """Tests for _require_entity_metadata(), which resolves a table's MetadataId or raises.""" + + async def test_returns_metadata_id(self): + """The MetadataId string is returned when the table exists in entity metadata.""" + client, od = _make_batch_client() + meta_id = await client._require_entity_metadata("account") + assert meta_id == "meta-1" + + async def test_not_found_raises(self): + """MetadataError is raised when the API returns no entity definition for the table.""" + client, od = _make_batch_client() + od._get_entity_by_table_schema_name = AsyncMock(return_value=None) + with pytest.raises(MetadataError): + await client._require_entity_metadata("nonexistent") + + +# --------------------------------------------------------------------------- +# _AsyncResponse +# --------------------------------------------------------------------------- + + +class TestHttpResponse: + """Tests for _AsyncResponse — the materialized response returned by _AsyncHttpClient.""" + + def test_json_parses_body(self): + """json() parses the body bytes as JSON.""" + r = _AsyncResponse(200, {"Content-Type": "application/json"}, b'{"value": []}') + assert r.json() == {"value": []} + + def test_json_empty_body_returns_empty_dict(self): + """json() returns {} for an empty body.""" + r = _AsyncResponse(200, {}, b"") + assert r.json() == {} + + def test_status_and_status_code_equal(self): + """status and status_code are both set and equal.""" + r = _AsyncResponse(207, {}, b"") + assert r.status == 207 + assert r.status_code == 207 + + def test_text_decodes_body(self): + """text property decodes body bytes as UTF-8.""" + r = _AsyncResponse(200, {}, b"body text") + assert r.text == "body text" + + def test_text_empty_body(self): + """text property returns empty string for empty body.""" + r = _AsyncResponse(200, {}, b"") + assert r.text == "" + + +# --------------------------------------------------------------------------- +# execute() edge cases +# --------------------------------------------------------------------------- + + +class TestExecuteEdgeCases: + """Tests for execute() error paths not covered by TestExecute.""" + + async def test_batch_size_exceeded_raises(self): + """execute() raises ValidationError when more than _MAX_BATCH_SIZE items are resolved.""" + client, od = _make_batch_client() + # _RecordCreate × (_MAX_BATCH_SIZE + 1) — each resolves to one request + items = [_RecordCreate(table="account", data={"name": f"X{i}"}) for i in range(_MAX_BATCH_SIZE + 1)] + with pytest.raises(ValidationError, match="exceeds the limit"): + await client.execute(items) + + async def test_response_passed_directly_to_parse_batch_response(self): + """execute() passes the HTTP response object directly to _parse_batch_response.""" + from PowerPlatform.Dataverse.models.batch import BatchResult + + client, od = _make_batch_client() + resp_mock = _batch_resp(status=200) + od._request = AsyncMock(return_value=resp_mock) + item = _RecordCreate(table="account", data={"name": "X"}) + with patch.object(client, "_parse_batch_response", return_value=BatchResult()) as mock_parse: + await client.execute([item]) + mock_parse.assert_called_once_with(resp_mock) + + +# --------------------------------------------------------------------------- +# _resolve_all() edge cases +# --------------------------------------------------------------------------- + + +class TestResolveAllEdgeCases: + """Tests for _resolve_all() paths not covered elsewhere.""" + + async def test_empty_changeset_is_skipped(self): + """An empty _ChangeSet is silently dropped from the resolved list.""" + client, od = _make_batch_client() + cs = _ChangeSet(_counter=[1]) + # No operations added — operations list is empty + result = await client._resolve_all([cs]) + assert result == [] + + async def test_non_changeset_item_extended(self): + """Non-changeset items are resolved and extended into the flat result.""" + client, od = _make_batch_client() + item = _RecordCreate(table="account", data={"name": "X"}) + result = await client._resolve_all([item]) + assert len(result) == 1 + + +# --------------------------------------------------------------------------- +# _resolve_item() full dispatch coverage +# --------------------------------------------------------------------------- + + +class TestResolveItemDispatch: + """One test per intent type — drives every branch of _resolve_item(). + + Each test replaces the specific resolver method with a mock so only the + dispatch logic is exercised. Record/SQL resolvers are async (awaited); + pure table resolvers inherited from _BatchBase are sync (not awaited). + """ + + _sentinel = MagicMock(method="GET", url="https://x/test", body=None, headers=None, content_id=None) + + def _async_mock(self): + return AsyncMock(return_value=[self._sentinel]) + + def _sync_mock(self): + return MagicMock(return_value=[self._sentinel]) + + async def test_dispatch_record_update(self): + client, _ = _make_batch_client() + client._resolve_record_update = self._async_mock() + result = await client._resolve_item(_RecordUpdate(table="account", ids="g", changes={"name": "X"})) + client._resolve_record_update.assert_called_once() + + async def test_dispatch_record_upsert(self): + client, _ = _make_batch_client() + client._resolve_record_upsert = self._async_mock() + item = UpsertItem(alternate_key={"k": "v"}, record={"name": "X"}) + result = await client._resolve_item(_RecordUpsert(table="account", items=[item])) + client._resolve_record_upsert.assert_called_once() + + async def test_dispatch_table_create(self): + client, _ = _make_batch_client() + client._resolve_table_create = self._sync_mock() + result = await client._resolve_item(_TableCreate(table="new_Test", columns={"new_Name": "string"})) + client._resolve_table_create.assert_called_once() + + async def test_dispatch_table_delete(self): + client, _ = _make_batch_client() + client._resolve_table_delete = self._async_mock() + result = await client._resolve_item(_TableDelete(table="new_Test")) + client._resolve_table_delete.assert_called_once() + + async def test_dispatch_table_get(self): + client, _ = _make_batch_client() + client._resolve_table_get = self._sync_mock() + result = await client._resolve_item(_TableGet(table="account")) + client._resolve_table_get.assert_called_once() + + async def test_dispatch_table_list(self): + client, _ = _make_batch_client() + client._resolve_table_list = self._sync_mock() + result = await client._resolve_item(_TableList()) + client._resolve_table_list.assert_called_once() + + async def test_dispatch_table_add_columns(self): + client, _ = _make_batch_client() + client._resolve_table_add_columns = self._async_mock() + result = await client._resolve_item(_TableAddColumns(table="account", columns={"new_X": "string"})) + client._resolve_table_add_columns.assert_called_once() + + async def test_dispatch_table_remove_columns(self): + client, _ = _make_batch_client() + client._resolve_table_remove_columns = self._async_mock() + result = await client._resolve_item(_TableRemoveColumns(table="account", columns="new_X")) + client._resolve_table_remove_columns.assert_called_once() + + async def test_dispatch_table_create_one_to_many(self): + client, _ = _make_batch_client() + client._resolve_table_create_one_to_many = self._sync_mock() + op = _TableCreateOneToMany(relationship=MagicMock(), lookup=MagicMock()) + result = await client._resolve_item(op) + client._resolve_table_create_one_to_many.assert_called_once() + + async def test_dispatch_table_create_many_to_many(self): + client, _ = _make_batch_client() + client._resolve_table_create_many_to_many = self._sync_mock() + op = _TableCreateManyToMany(relationship=MagicMock()) + result = await client._resolve_item(op) + client._resolve_table_create_many_to_many.assert_called_once() + + async def test_dispatch_table_delete_relationship(self): + client, _ = _make_batch_client() + client._resolve_table_delete_relationship = self._sync_mock() + result = await client._resolve_item(_TableDeleteRelationship(relationship_id="rel-guid")) + client._resolve_table_delete_relationship.assert_called_once() + + async def test_dispatch_table_get_relationship(self): + client, _ = _make_batch_client() + client._resolve_table_get_relationship = self._sync_mock() + result = await client._resolve_item(_TableGetRelationship(schema_name="new_a_c")) + client._resolve_table_get_relationship.assert_called_once() + + async def test_dispatch_table_create_lookup_field(self): + client, _ = _make_batch_client() + client._resolve_table_create_lookup_field = self._sync_mock() + result = await client._resolve_item( + _TableCreateLookupField( + referencing_table="contact", + lookup_field_name="new_accountid", + referenced_table="account", + ) + ) + client._resolve_table_create_lookup_field.assert_called_once() + + async def test_dispatch_query_sql(self): + client, _ = _make_batch_client() + client._resolve_query_sql = self._async_mock() + result = await client._resolve_item(_QuerySql(sql="SELECT accountid FROM account")) + client._resolve_query_sql.assert_called_once() + + async def test_dispatch_unknown_type_raises(self): + """An unrecognised intent type raises ValidationError.""" + client, _ = _make_batch_client() + with pytest.raises(ValidationError, match="Unknown batch item type"): + await client._resolve_item("not-an-intent") diff --git a/tests/unit/aio/data/test_async_odata_internal.py b/tests/unit/aio/data/test_async_odata_internal.py new file mode 100644 index 00000000..a064c1ed --- /dev/null +++ b/tests/unit/aio/data/test_async_odata_internal.py @@ -0,0 +1,1846 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +"""Unit tests for _AsyncODataClient internals (mocking _request at the HTTP boundary).""" + +import json +import time +import warnings +from unittest.mock import AsyncMock, MagicMock, patch + +import pytest + +from PowerPlatform.Dataverse.aio.data._async_odata import _AsyncODataClient +from PowerPlatform.Dataverse.core.errors import HttpError, MetadataError, ValidationError + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + + +def _make_client() -> _AsyncODataClient: + """Return _AsyncODataClient with _request mocked out at the HTTP boundary.""" + auth = MagicMock() + auth._acquire_token = AsyncMock(return_value=MagicMock(access_token="test-token")) + client = _AsyncODataClient(auth, "https://example.crm.dynamics.com") + client._request = AsyncMock() + return client + + +def _resp(json_data=None, status=200, headers=None): + """Create a mock _AsyncResponse-compatible response.""" + r = MagicMock() + r.status = status + r.headers = headers or {} + r.text = json.dumps(json_data) if json_data is not None else "" + r.json = MagicMock(return_value=json_data if json_data is not None else {}) + return r + + +def _entity_def( + entity_set="accounts", + pk="accountid", + meta_id="meta-001", + schema="Account", + logical="account", +): + """Return a minimal EntityDefinitions value-list response body.""" + return { + "value": [ + { + "LogicalName": logical, + "EntitySetName": entity_set, + "PrimaryIdAttribute": pk, + "MetadataId": meta_id, + "SchemaName": schema, + } + ] + } + + +def _seed_cache(client: _AsyncODataClient, table="account", entity_set="accounts", pk="accountid"): + """Pre-populate entity-set and primary-ID caches to bypass HTTP for schema-name lookups.""" + key = client._normalize_cache_key(table) + client._logical_to_entityset_cache[key] = entity_set + client._logical_primaryid_cache[key] = pk + + +# --------------------------------------------------------------------------- +# close() +# --------------------------------------------------------------------------- + + +class TestClose: + """Tests for the close() lifecycle method.""" + + async def test_close_delegates_to_http(self): + """close() forwards to the underlying HTTP client's close() exactly once.""" + client = _make_client() + client._http.close = AsyncMock() + await client.close() + client._http.close.assert_called_once() + + async def test_close_clears_entity_set_cache(self): + """close() empties the entity-set lookup cache so stale entries don't persist.""" + client = _make_client() + _seed_cache(client) + client._http.close = AsyncMock() + await client.close() + assert len(client._logical_to_entityset_cache) == 0 + + +# --------------------------------------------------------------------------- +# _request() — tests actual implementation via _raw_request mock +# --------------------------------------------------------------------------- + + +class TestRequest: + """Tests for _request() error extraction. + + These tests mock _raw_request (one level below _request) so the real + header-building, status-checking, and error-parsing code runs. + """ + + def _auth_client(self): + """Return a client with a real auth mock but _raw_request not yet patched.""" + auth = MagicMock() + auth._acquire_token = AsyncMock(return_value=MagicMock(access_token="token")) + return _AsyncODataClient(auth, "https://example.crm.dynamics.com") + + async def test_ok_response_returned(self): + """2xx responses are returned to the caller without raising.""" + client = self._auth_client() + client._raw_request = AsyncMock(return_value=_resp(status=200, json_data={"value": []})) + r = await client._request("get", "https://example.crm.dynamics.com/api/data/v9.2/accounts") + assert r.status == 200 + + async def test_error_with_nested_error_object(self): + """Nested error.code / error.message body structure is parsed into HttpError.""" + client = self._auth_client() + body = {"error": {"code": "0x80040265", "message": "Not found"}} + client._raw_request = AsyncMock(return_value=_resp(status=404, json_data=body)) + with pytest.raises(HttpError) as exc: + await client._request("get", "https://example.crm.dynamics.com/api/data/v9.2/accounts") + assert exc.value.status_code == 404 + assert "Not found" in str(exc.value) + + async def test_error_with_message_at_root(self): + """A top-level message key in the body is used when error nesting is absent.""" + client = self._auth_client() + body = {"message": "Root-level message"} + client._raw_request = AsyncMock(return_value=_resp(status=400, json_data=body)) + with pytest.raises(HttpError) as exc: + await client._request("get", "https://example.crm.dynamics.com/api/data/v9.2/accounts") + assert "Root-level message" in str(exc.value) + + async def test_error_non_json_body_handled(self): + """Non-JSON response body falls back to HTTP status code as the error message.""" + client = self._auth_client() + r = MagicMock() + r.status = 503 + r.headers = {} + r.text = "Service Unavailable" + client._raw_request = AsyncMock(return_value=r) + with pytest.raises(HttpError) as exc: + await client._request("get", "https://example.crm.dynamics.com/api/data/v9.2/accounts") + assert exc.value.status_code == 503 + + async def test_retry_after_header_parsed(self): + """Retry-After header value is stored as an integer in the error's details dict.""" + client = self._auth_client() + body = {"error": {"code": "429", "message": "Too many requests"}} + r = _resp(status=429, json_data=body, headers={"Retry-After": "60"}) + client._raw_request = AsyncMock(return_value=r) + with pytest.raises(HttpError) as exc: + await client._request("get", "https://example.crm.dynamics.com/api/data/v9.2/accounts") + assert exc.value.to_dict()["details"].get("retry_after") == 60 + + async def test_service_request_id_extracted(self): + """x-ms-service-request-id header is stored in the error's details dict.""" + client = self._auth_client() + r = _resp( + status=500, + json_data={"error": {"code": "err", "message": "fail"}}, + headers={"x-ms-service-request-id": "srv-req-1"}, + ) + client._raw_request = AsyncMock(return_value=r) + with pytest.raises(HttpError) as exc: + await client._request("get", "https://example.crm.dynamics.com/api/data/v9.2/accounts") + assert exc.value.to_dict()["details"].get("service_request_id") == "srv-req-1" + + +# --------------------------------------------------------------------------- +# _create() +# --------------------------------------------------------------------------- + + +class TestCreate: + """Tests for _create() single-record creation.""" + + async def test_returns_guid_from_odata_entity_id(self): + """GUID is extracted from the OData-EntityId response header.""" + client = _make_client() + _seed_cache(client) + guid = "12345678-1234-1234-1234-123456789012" + client._request.return_value = _resp( + status=204, + headers={"OData-EntityId": f"https://example.crm.dynamics.com/api/data/v9.2/accounts({guid})"}, + ) + result = await client._create("accounts", "account", {"amount": 100}) + assert result == guid + + async def test_returns_guid_from_location_header(self): + """Location header is used as fallback when OData-EntityId is absent.""" + client = _make_client() + _seed_cache(client) + guid = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" + client._request.return_value = _resp( + status=204, + headers={"Location": f"https://example.crm.dynamics.com/api/data/v9.2/accounts({guid})"}, + ) + result = await client._create("accounts", "account", {"amount": 100}) + assert result == guid + + async def test_raises_when_no_guid_in_headers(self): + """RuntimeError is raised when neither OData-EntityId nor Location contains a GUID.""" + client = _make_client() + _seed_cache(client) + client._request.return_value = _resp(status=204, headers={}) + with pytest.raises(RuntimeError, match="GUID"): + await client._create("accounts", "account", {"amount": 100}) + + +# --------------------------------------------------------------------------- +# _create_multiple() +# --------------------------------------------------------------------------- + + +class TestCreateMultiple: + """Tests for _create_multiple() bulk record creation.""" + + async def test_returns_ids_from_ids_key(self): + """IDs are extracted from the top-level Ids key in the response body.""" + client = _make_client() + _seed_cache(client) + guids = ["11111111-1111-1111-1111-111111111111", "22222222-2222-2222-2222-222222222222"] + client._request.return_value = _resp(json_data={"Ids": guids}, status=200) + result = await client._create_multiple("accounts", "account", [{"amount": 1}, {"amount": 2}]) + assert result == guids + + async def test_returns_ids_from_value_list(self): + """IDs are extracted from value list entries when the Ids key is absent.""" + client = _make_client() + _seed_cache(client) + guid = "11111111-1111-1111-1111-111111111111" + client._request.return_value = _resp(json_data={"value": [{"accountid": guid}]}, status=200) + result = await client._create_multiple("accounts", "account", [{"amount": 1}]) + assert result == [guid] + + async def test_empty_body_returns_empty_list(self): + """An empty response body returns an empty list without raising.""" + client = _make_client() + _seed_cache(client) + client._request.return_value = _resp(json_data={}, status=200) + result = await client._create_multiple("accounts", "account", [{"amount": 1}]) + assert result == [] + + async def test_non_dict_records_raises(self): + """TypeError is raised when the records list contains non-dict items.""" + client = _make_client() + _seed_cache(client) + with pytest.raises(TypeError): + await client._create_multiple("accounts", "account", ["not-a-dict"]) + + +# --------------------------------------------------------------------------- +# _update() / _update_by_ids() / _update_multiple() +# --------------------------------------------------------------------------- + + +class TestUpdate: + """Tests for _update(), _update_by_ids(), and _update_multiple().""" + + async def test_update_patches_record(self): + """_update() issues a PATCH request for the given record ID.""" + client = _make_client() + _seed_cache(client) + client._request.return_value = _resp(status=204) + await client._update("account", "guid-1", {"telephone1": "555"}) + assert client._request.called + + async def test_update_by_ids_broadcast_dict(self): + """A dict for changes is broadcast to all IDs via UpdateMultiple.""" + client = _make_client() + _seed_cache(client) + client._request.return_value = _resp(status=204) + await client._update_by_ids("account", ["id-1", "id-2"], {"statecode": 0}) + assert client._request.called + + async def test_update_by_ids_paired_list(self): + """A list for changes is applied pairwise with the corresponding IDs.""" + client = _make_client() + _seed_cache(client) + client._request.return_value = _resp(status=204) + await client._update_by_ids("account", ["id-1"], [{"name": "A"}]) + assert client._request.called + + async def test_update_by_ids_empty_list_is_noop(self): + """An empty ID list short-circuits without issuing any HTTP request.""" + client = _make_client() + result = await client._update_by_ids("account", [], {"statecode": 0}) + assert result is None + client._request.assert_not_called() + + async def test_update_by_ids_mismatched_length_raises(self): + """ValueError is raised when the changes list is shorter than the ID list.""" + client = _make_client() + _seed_cache(client) + client._request.return_value = _resp(status=200) + with pytest.raises(ValueError): + await client._update_by_ids("account", ["id-1", "id-2"], [{"name": "A"}]) + + async def test_update_by_ids_invalid_changes_type_raises(self): + """TypeError is raised when changes is neither a dict nor a list.""" + client = _make_client() + _seed_cache(client) + with pytest.raises(TypeError): + await client._update_by_ids("account", ["id-1"], "invalid") + + async def test_update_multiple_empty_records_raises(self): + """TypeError is raised when the records list is empty.""" + client = _make_client() + with pytest.raises(TypeError): + await client._update_multiple("accounts", "account", []) + + async def test_update_multiple_non_list_raises(self): + """TypeError is raised when the records argument is not a list.""" + client = _make_client() + with pytest.raises(TypeError): + await client._update_multiple("accounts", "account", "not-a-list") + + +# --------------------------------------------------------------------------- +# _delete() / _delete_multiple() +# --------------------------------------------------------------------------- + + +class TestDelete: + """Tests for _delete() single-record deletion and _delete_multiple() bulk deletion.""" + + async def test_delete_calls_request(self): + """_delete() issues a DELETE request for the given record ID.""" + client = _make_client() + _seed_cache(client) + client._request.return_value = _resp(status=204) + await client._delete("account", "guid-1") + assert client._request.called + + async def test_delete_multiple_returns_job_id(self): + """JobId from the async BulkDelete response is returned to the caller.""" + client = _make_client() + _seed_cache(client) + client._request.return_value = _resp(json_data={"JobId": "job-guid-1"}, status=202) + result = await client._delete_multiple("account", ["id-1", "id-2"]) + assert result == "job-guid-1" + + async def test_delete_multiple_empty_ids_returns_none(self): + """An empty ID list short-circuits without issuing any HTTP request.""" + client = _make_client() + result = await client._delete_multiple("account", []) + assert result is None + client._request.assert_not_called() + + async def test_delete_multiple_no_job_id_in_body(self): + """None is returned when the response body does not contain a JobId key.""" + client = _make_client() + _seed_cache(client) + client._request.return_value = _resp(json_data={}, status=204) + result = await client._delete_multiple("account", ["id-1"]) + assert result is None + + +# --------------------------------------------------------------------------- +# _get() / _get_multiple() +# --------------------------------------------------------------------------- + + +class TestGet: + """Tests for _get() single-record fetch.""" + + async def test_get_returns_record(self): + """The full record dict from the response body is returned unchanged.""" + client = _make_client() + _seed_cache(client) + record = {"accountid": "guid-1", "name": "Contoso"} + client._request.return_value = _resp(json_data=record, status=200) + result = await client._get("account", "guid-1") + assert result == record + + async def test_get_with_select_param(self): + """The select list is forwarded as a $select query parameter.""" + client = _make_client() + _seed_cache(client) + client._request.return_value = _resp(json_data={"name": "Contoso"}, status=200) + result = await client._get("account", "guid-1", select=["name"]) + assert result == {"name": "Contoso"} + + +class TestGetMultiple: + """Tests for _get_multiple() async generator for paged results.""" + + async def test_single_page_yielded(self): + """A single-page response produces exactly one batch from the generator.""" + client = _make_client() + _seed_cache(client) + page = {"value": [{"accountid": "1"}, {"accountid": "2"}]} + client._request.return_value = _resp(json_data=page, status=200) + pages = [] + async for p in client._get_multiple("account"): + pages.append(p) + assert len(pages) == 1 + assert len(pages[0]) == 2 + + async def test_follows_next_link(self): + """@odata.nextLink is followed to fetch subsequent pages automatically.""" + client = _make_client() + _seed_cache(client) + next_url = "https://example.crm.dynamics.com/api/data/v9.2/accounts?$skiptoken=xyz" + page1 = {"value": [{"accountid": "1"}], "@odata.nextLink": next_url} + page2 = {"value": [{"accountid": "2"}]} + client._request.side_effect = [_resp(json_data=page1), _resp(json_data=page2)] + pages = [] + async for p in client._get_multiple("account"): + pages.append(p) + assert len(pages) == 2 + + async def test_empty_value_not_yielded(self): + """A page with an empty value list produces no output from the generator.""" + client = _make_client() + _seed_cache(client) + client._request.return_value = _resp(json_data={"value": []}, status=200) + pages = [] + async for p in client._get_multiple("account"): + pages.append(p) + assert len(pages) == 0 + + async def test_with_all_params(self): + """All optional query parameters are forwarded in the outbound request.""" + client = _make_client() + _seed_cache(client) + client._request.return_value = _resp(json_data={"value": []}, status=200) + async for _ in client._get_multiple( + "account", + select=["name"], + filter="statecode eq 0", + orderby=["name asc"], + top=10, + expand=["primarycontactid"], + page_size=5, + count=True, + include_annotations="*", + ): + pass + call = client._request.call_args + assert call is not None + kwargs = call.kwargs + assert "headers" in kwargs or kwargs.get("params") is not None + + +# --------------------------------------------------------------------------- +# _query_sql() +# --------------------------------------------------------------------------- + + +class TestQuerySql: + """Tests for _query_sql() which executes Dataverse SQL against the TDS endpoint.""" + + async def test_raises_if_not_string(self): + """ValidationError is raised when the SQL argument is not a string.""" + client = _make_client() + with pytest.raises(ValidationError): + await client._query_sql(123) + + async def test_raises_if_empty(self): + """ValidationError is raised for a blank or whitespace-only SQL string.""" + client = _make_client() + with pytest.raises(ValidationError): + await client._query_sql(" ") + + async def test_returns_rows_from_value(self): + """Rows are extracted from the value list in a standard OData response body.""" + client = _make_client() + _seed_cache(client) + rows = [{"name": "A"}, {"name": "B"}] + client._request.return_value = _resp(json_data={"value": rows}, status=200) + result = await client._query_sql("SELECT name FROM account") + assert result == rows + + async def test_returns_list_body_directly(self): + """A list response body (rather than {value: [...]}) is accepted as rows directly.""" + client = _make_client() + _seed_cache(client) + rows = [{"name": "A"}] + client._request.return_value = _resp(json_data=rows, status=200) + result = await client._query_sql("SELECT name FROM account") + assert result == rows + + async def test_follows_next_link(self): + """Pagination via @odata.nextLink concatenates all rows across pages.""" + client = _make_client() + _seed_cache(client) + next_url = "https://example.crm.dynamics.com/api/data/v9.2/accounts?sql=SELECT+name+FROM+account&page=2" + page1 = {"value": [{"name": "A"}], "@odata.nextLink": next_url} + page2 = {"value": [{"name": "B"}]} + client._request.side_effect = [_resp(json_data=page1), _resp(json_data=page2)] + result = await client._query_sql("SELECT name FROM account") + assert len(result) == 2 + + async def test_warns_and_stops_on_url_cycle(self): + """A repeated nextLink triggers a warning and stops pagination to prevent an infinite loop.""" + client = _make_client() + _seed_cache(client) + cycle_url = "https://example.crm.dynamics.com/api/data/v9.2/accounts?sql=SELECT+name+FROM+account&page=1" + page1 = {"value": [{"name": "A"}], "@odata.nextLink": cycle_url} + page2 = {"value": [{"name": "B"}], "@odata.nextLink": cycle_url} + client._request.side_effect = [_resp(json_data=page1), _resp(json_data=page2)] + with warnings.catch_warnings(record=True) as caught: + warnings.simplefilter("always") + result = await client._query_sql("SELECT name FROM account") + # Cycle detected after page 2's nextLink repeats; pages 1 and 2 are collected. + assert any("same nextLink" in str(w.message) for w in caught) + assert len(result) == 2 + + async def test_stops_on_non_dict_page_body(self): + """A non-dict page body halts pagination and discards the malformed page.""" + client = _make_client() + _seed_cache(client) + next_url = "https://example.crm.dynamics.com/api/data/v9.2/accounts?sql=SELECT+name&page=2" + page1 = {"value": [{"name": "A"}], "@odata.nextLink": next_url} + # page2 is a list, not a dict — break condition + client._request.side_effect = [_resp(json_data=page1), _resp(json_data=["not-a-dict"])] + result = await client._query_sql("SELECT name FROM account") + assert len(result) == 1 + + +# --------------------------------------------------------------------------- +# _entity_set_from_schema_name() +# --------------------------------------------------------------------------- + + +class TestEntitySetResolution: + """Tests for _entity_set_from_schema_name() cache lookup and HTTP fetch.""" + + async def test_cache_hit_skips_http(self): + """A pre-populated cache entry is returned without any HTTP call.""" + client = _make_client() + _seed_cache(client) + result = await client._entity_set_from_schema_name("account") + client._request.assert_not_called() + assert result == "accounts" + + async def test_fetches_and_caches(self): + """On a cache miss, the entity set is fetched from the API and cached for reuse.""" + client = _make_client() + client._request.return_value = _resp(json_data=_entity_def(), status=200) + result = await client._entity_set_from_schema_name("account") + assert result == "accounts" + # Subsequent call must hit the cache, not the API. + client._request.reset_mock() + result2 = await client._entity_set_from_schema_name("account") + client._request.assert_not_called() + assert result2 == "accounts" + + async def test_caches_primary_id_attr(self): + """The primary ID attribute is cached alongside the entity set name.""" + client = _make_client() + client._request.return_value = _resp(json_data=_entity_def(pk="accountid"), status=200) + await client._entity_set_from_schema_name("account") + key = client._normalize_cache_key("account") + assert client._logical_primaryid_cache.get(key) == "accountid" + + async def test_not_found_raises_metadata_error(self): + """MetadataError is raised when the API returns an empty value list.""" + client = _make_client() + client._request.return_value = _resp(json_data={"value": []}, status=200) + with pytest.raises(MetadataError, match="Unable to resolve"): + await client._entity_set_from_schema_name("nonexistent") + + async def test_plural_name_includes_hint(self): + """The error message hints at a plural-name mistake when the input ends with 's'.""" + client = _make_client() + client._request.return_value = _resp(json_data={"value": []}, status=200) + with pytest.raises(MetadataError, match="plural"): + await client._entity_set_from_schema_name("accounts") + + async def test_missing_entity_set_name_raises(self): + """MetadataError is raised when the entity definition lacks an EntitySetName.""" + client = _make_client() + client._request.return_value = _resp( + json_data={"value": [{"LogicalName": "account", "MetadataId": "m1"}]}, + status=200, + ) + with pytest.raises(MetadataError, match="EntitySetName"): + await client._entity_set_from_schema_name("account") + + async def test_empty_name_raises_value_error(self): + """ValueError is raised immediately for an empty table schema name.""" + client = _make_client() + with pytest.raises(ValueError): + await client._entity_set_from_schema_name("") + + +# --------------------------------------------------------------------------- +# _get_table_info() / _list_tables() / _delete_table() +# --------------------------------------------------------------------------- + + +class TestTableInfo: + """Tests for _get_table_info() entity-definition summary lookup.""" + + async def test_get_table_info_found(self): + """A found table returns a dict containing entity_set_name and columns_created.""" + client = _make_client() + client._request.return_value = _resp(json_data=_entity_def(), status=200) + result = await client._get_table_info("account") + assert result is not None + assert result["entity_set_name"] == "accounts" + assert result["columns_created"] == [] + + async def test_get_table_info_not_found(self): + """None is returned when the table does not exist in metadata.""" + client = _make_client() + client._request.return_value = _resp(json_data={"value": []}, status=200) + result = await client._get_table_info("nonexistent") + assert result is None + + +class TestListTables: + """Tests for _list_tables() entity-definition list retrieval.""" + + async def test_list_tables_returns_value(self): + """The value list from the EntityDefinitions response is returned unchanged.""" + client = _make_client() + tables = [{"LogicalName": "account"}] + client._request.return_value = _resp(json_data={"value": tables}, status=200) + result = await client._list_tables() + assert result == tables + + async def test_list_tables_with_filter_and_select(self): + """Optional filter and select parameters are forwarded to the API request.""" + client = _make_client() + client._request.return_value = _resp(json_data={"value": []}, status=200) + result = await client._list_tables(filter="IsPrivate eq false", select=["LogicalName"]) + assert result == [] + + +class TestDeleteTable: + """Tests for _delete_table() metadata-level table removal.""" + + async def test_delete_calls_delete_request(self): + """Two requests are issued: one to resolve the MetadataId, one DELETE.""" + client = _make_client() + client._request.side_effect = [_resp(json_data=_entity_def()), _resp(status=204)] + await client._delete_table("account") + assert client._request.call_count == 2 + + async def test_delete_not_found_raises(self): + """MetadataError is raised when the target table does not exist.""" + client = _make_client() + client._request.return_value = _resp(json_data={"value": []}, status=200) + with pytest.raises(MetadataError, match="not found"): + await client._delete_table("nonexistent") + + +# --------------------------------------------------------------------------- +# _create_table() +# --------------------------------------------------------------------------- + + +class TestCreateTable: + """Tests for _create_table() custom table provisioning.""" + + async def test_table_already_exists_raises(self): + """MetadataError is raised when a table with the same schema name already exists.""" + client = _make_client() + client._request.return_value = _resp(json_data=_entity_def(), status=200) + with pytest.raises(MetadataError, match="already exists"): + await client._create_table("account", {}) + + async def test_success_with_columns(self): + """Table and typed columns are created; the returned dict lists columns_created.""" + client = _make_client() + not_found = _resp(json_data={"value": []}, status=200) + create_resp = _resp(status=204) + entity_resp = _resp( + json_data=_entity_def(entity_set="new_products", schema="new_Product", logical="new_product"), + status=200, + ) + client._request.side_effect = [not_found, create_resp, entity_resp] + result = await client._create_table("new_Product", {"new_Price": "decimal"}) + assert result["table_schema_name"] == "new_Product" + assert "new_Price" in result["columns_created"] + + async def test_success_with_primary_column(self): + """An explicit primary_column_schema_name is accepted without error.""" + client = _make_client() + not_found = _resp(json_data={"value": []}, status=200) + create_resp = _resp(status=204) + entity_resp = _resp(json_data=_entity_def(entity_set="new_products"), status=200) + client._request.side_effect = [not_found, create_resp, entity_resp] + result = await client._create_table("new_Product", {}, primary_column_schema_name="new_ProductName") + assert result is not None + + async def test_success_with_display_name(self): + """A string display_name is accepted and forwarded to the API.""" + client = _make_client() + not_found = _resp(json_data={"value": []}, status=200) + create_resp = _resp(status=204) + entity_resp = _resp(json_data=_entity_def(entity_set="new_products"), status=200) + client._request.side_effect = [not_found, create_resp, entity_resp] + result = await client._create_table("new_Product", {}, display_name="Product") + assert result is not None + + async def test_unsupported_column_type_raises(self): + """ValueError is raised before the POST when a column type string is unrecognised.""" + client = _make_client() + client._request.return_value = _resp(json_data={"value": []}, status=200) + with pytest.raises(ValueError, match="Unsupported"): + await client._create_table("new_Product", {"col": "badtype"}) + + async def test_empty_solution_name_raises(self): + """ValueError is raised when solution_unique_name is an empty string.""" + client = _make_client() + client._request.return_value = _resp(json_data={"value": []}, status=200) + with pytest.raises(ValueError, match="cannot be empty"): + await client._create_table("new_Product", {}, solution_unique_name="") + + async def test_non_string_solution_raises(self): + """TypeError is raised when solution_unique_name is not a string.""" + client = _make_client() + client._request.return_value = _resp(json_data={"value": []}, status=200) + with pytest.raises(TypeError): + await client._create_table("new_Product", {}, solution_unique_name=42) + + async def test_invalid_display_name_raises(self): + """TypeError is raised when display_name is not a string.""" + client = _make_client() + client._request.return_value = _resp(json_data={"value": []}, status=200) + with pytest.raises(TypeError): + await client._create_table("new_Product", {}, display_name=123) + + +# --------------------------------------------------------------------------- +# _create_columns() / _delete_columns() +# --------------------------------------------------------------------------- + + +class TestCreateColumns: + """Tests for _create_columns() column provisioning on an existing table.""" + + async def test_creates_string_column(self): + """A string-typed column is created and its name returned in the result list.""" + client = _make_client() + entity_resp = _resp(json_data=_entity_def(), status=200) + attr_resp = _resp(status=204) + client._request.side_effect = [entity_resp, attr_resp] + result = await client._create_columns("account", {"new_Notes": "string"}) + assert result == ["new_Notes"] + + async def test_empty_columns_dict_raises(self): + """TypeError is raised when the columns dict is empty.""" + client = _make_client() + with pytest.raises(TypeError, match="non-empty dict"): + await client._create_columns("account", {}) + + async def test_non_dict_columns_raises(self): + """TypeError is raised when the columns argument is not a dict.""" + client = _make_client() + with pytest.raises(TypeError): + await client._create_columns("account", ["col"]) + + async def test_table_not_found_raises(self): + """MetadataError is raised when the parent table does not exist.""" + client = _make_client() + client._request.return_value = _resp(json_data={"value": []}, status=200) + with pytest.raises(MetadataError, match="not found"): + await client._create_columns("nonexistent", {"col": "string"}) + + async def test_unsupported_type_raises_validation_error(self): + """ValidationError is raised for an unrecognised column type string.""" + client = _make_client() + entity_resp = _resp(json_data=_entity_def(), status=200) + client._request.return_value = entity_resp + with pytest.raises(ValidationError): + await client._create_columns("account", {"col": "badtype"}) + + async def test_optionset_column_flushes_cache(self): + """Creating a column whose payload includes OptionSet invalidates the picklist label cache. + + Boolean columns produce an OptionSet payload, which triggers the same cache-flush + path used by choice/picklist columns. + """ + client = _make_client() + entity_resp = _resp(json_data=_entity_def(), status=200) + attr_resp = _resp(status=204) + client._request.side_effect = [entity_resp, attr_resp] + key = client._normalize_cache_key("account") + client._picklist_label_cache[key] = {"ts": time.time(), "picklists": {"old": {}}} + result = await client._create_columns("account", {"new_Status": "bool"}) + assert result == ["new_Status"] + assert key not in client._picklist_label_cache + + +class TestDeleteColumns: + """Tests for _delete_columns() column removal from an existing table.""" + + async def test_string_column_name(self): + """A single column name supplied as a string is accepted and deleted.""" + client = _make_client() + entity_resp = _resp(json_data=_entity_def(), status=200) + attr_resp = _resp(json_data={"value": [{"MetadataId": "attr-1", "LogicalName": "new_notes"}]}, status=200) + delete_resp = _resp(status=204) + client._request.side_effect = [entity_resp, attr_resp, delete_resp] + result = await client._delete_columns("account", "new_Notes") + assert result == ["new_Notes"] + + async def test_list_column_names(self): + """Column names supplied as a list are each deleted in turn.""" + client = _make_client() + entity_resp = _resp(json_data=_entity_def(), status=200) + attr_resp = _resp(json_data={"value": [{"MetadataId": "attr-1", "LogicalName": "new_notes"}]}, status=200) + delete_resp = _resp(status=204) + client._request.side_effect = [entity_resp, attr_resp, delete_resp] + result = await client._delete_columns("account", ["new_Notes"]) + assert result == ["new_Notes"] + + async def test_invalid_type_raises(self): + """TypeError is raised when the columns argument is neither str nor list.""" + client = _make_client() + with pytest.raises(TypeError): + await client._delete_columns("account", 42) + + async def test_empty_column_name_raises(self): + """ValueError is raised when the column name string is empty.""" + client = _make_client() + with pytest.raises(ValueError, match="non-empty"): + await client._delete_columns("account", "") + + async def test_table_not_found_raises(self): + """MetadataError is raised when the parent table does not exist.""" + client = _make_client() + client._request.return_value = _resp(json_data={"value": []}, status=200) + with pytest.raises(MetadataError): + await client._delete_columns("nonexistent", "col") + + async def test_column_not_found_raises(self): + """MetadataError is raised when the column is absent from attribute metadata.""" + client = _make_client() + entity_resp = _resp(json_data=_entity_def(), status=200) + attr_resp = _resp(json_data={"value": []}, status=200) + client._request.side_effect = [entity_resp, attr_resp] + with pytest.raises(MetadataError, match="not found"): + await client._delete_columns("account", "nonexistent_col") + + async def test_missing_attr_metadata_id_raises(self): + """RuntimeError is raised when the attribute response lacks a MetadataId.""" + client = _make_client() + entity_resp = _resp(json_data=_entity_def(), status=200) + attr_resp = _resp(json_data={"value": [{"LogicalName": "new_notes"}]}, status=200) + client._request.side_effect = [entity_resp, attr_resp] + with pytest.raises(RuntimeError, match="MetadataId"): + await client._delete_columns("account", "new_Notes") + + async def test_picklist_column_flushes_cache(self): + """Deleting a Picklist-type column invalidates the picklist label cache.""" + client = _make_client() + entity_resp = _resp(json_data=_entity_def(), status=200) + attr_resp = _resp( + json_data={"value": [{"MetadataId": "attr-1", "LogicalName": "new_status", "AttributeType": "Picklist"}]}, + status=200, + ) + delete_resp = _resp(status=204) + client._request.side_effect = [entity_resp, attr_resp, delete_resp] + key = client._normalize_cache_key("account") + client._picklist_label_cache[key] = {"ts": time.time(), "picklists": {}} + result = await client._delete_columns("account", "new_Status") + assert result == ["new_Status"] + assert key not in client._picklist_label_cache + + +# --------------------------------------------------------------------------- +# _list_columns() +# --------------------------------------------------------------------------- + + +class TestListColumns: + """Tests for _list_columns() attribute metadata listing.""" + + async def test_returns_attribute_list(self): + """The full attribute list from the API response is returned.""" + client = _make_client() + entity_resp = _resp(json_data=_entity_def(), status=200) + cols_resp = _resp(json_data={"value": [{"LogicalName": "name"}, {"LogicalName": "accountid"}]}, status=200) + client._request.side_effect = [entity_resp, cols_resp] + result = await client._list_columns("account") + assert len(result) == 2 + + async def test_table_not_found_raises(self): + """MetadataError is raised when the table is absent from metadata.""" + client = _make_client() + client._request.return_value = _resp(json_data={"value": []}, status=200) + with pytest.raises(MetadataError, match="not found"): + await client._list_columns("nonexistent") + + async def test_with_select_and_filter(self): + """Optional select and filter parameters are forwarded to the Attributes API call.""" + client = _make_client() + entity_resp = _resp(json_data=_entity_def(), status=200) + cols_resp = _resp(json_data={"value": []}, status=200) + client._request.side_effect = [entity_resp, cols_resp] + result = await client._list_columns("account", select=["LogicalName"], filter="AttributeType eq 'String'") + assert result == [] + + +# --------------------------------------------------------------------------- +# Alternate key operations +# --------------------------------------------------------------------------- + + +class TestAlternateKeys: + """Tests for _create_alternate_key(), _get_alternate_keys(), and _delete_alternate_key().""" + + async def test_create_alternate_key_success(self): + """The key UUID is extracted from the OData-EntityId header and returned in metadata_id. + + The URL format is EntityDefinitions(LogicalName='...')/Keys(uuid), so the regex + skips the LogicalName= form and matches only the key UUID in parentheses. + """ + client = _make_client() + key_uuid = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" + entity_resp = _resp(json_data=_entity_def(), status=200) + create_resp = _resp( + status=204, + headers={ + "OData-EntityId": f"https://example.crm.dynamics.com/api/data/v9.2/EntityDefinitions(LogicalName='account')/Keys({key_uuid})" + }, + ) + client._request.side_effect = [entity_resp, create_resp] + result = await client._create_alternate_key("account", "new_prod_key", ["new_productcode"]) + assert result["schema_name"] == "new_prod_key" + assert result["key_attributes"] == ["new_productcode"] + assert result["metadata_id"] == key_uuid + + async def test_create_alternate_key_table_not_found_raises(self): + """MetadataError is raised when the target table does not exist.""" + client = _make_client() + client._request.return_value = _resp(json_data={"value": []}, status=200) + with pytest.raises(MetadataError): + await client._create_alternate_key("nonexistent", "key", ["col"]) + + async def test_get_alternate_keys_returns_list(self): + """All alternate keys on the table are returned as a list of dicts.""" + client = _make_client() + entity_resp = _resp(json_data=_entity_def(), status=200) + keys_resp = _resp(json_data={"value": [{"SchemaName": "key1"}, {"SchemaName": "key2"}]}, status=200) + client._request.side_effect = [entity_resp, keys_resp] + result = await client._get_alternate_keys("account") + assert len(result) == 2 + + async def test_get_alternate_keys_table_not_found_raises(self): + """MetadataError is raised when the table does not exist.""" + client = _make_client() + client._request.return_value = _resp(json_data={"value": []}, status=200) + with pytest.raises(MetadataError): + await client._get_alternate_keys("nonexistent") + + async def test_delete_alternate_key_success(self): + """Two requests are issued: one entity lookup then one DELETE for the key.""" + client = _make_client() + entity_resp = _resp(json_data=_entity_def(), status=200) + delete_resp = _resp(status=204) + client._request.side_effect = [entity_resp, delete_resp] + await client._delete_alternate_key("account", "key-guid") + assert client._request.call_count == 2 + + async def test_delete_alternate_key_table_not_found_raises(self): + """MetadataError is raised when the table does not exist.""" + client = _make_client() + client._request.return_value = _resp(json_data={"value": []}, status=200) + with pytest.raises(MetadataError): + await client._delete_alternate_key("nonexistent", "key-guid") + + +# --------------------------------------------------------------------------- +# _upsert() / _upsert_multiple() +# --------------------------------------------------------------------------- + + +class TestUpsert: + """Tests for _upsert() and _upsert_multiple() alternate-key upsert operations.""" + + async def test_upsert_issues_patch(self): + """_upsert() issues a PATCH request (create-or-replace semantics).""" + client = _make_client() + _seed_cache(client) + client._request.return_value = _resp(status=204) + await client._upsert("accounts", "account", {"accountnumber": "A"}, {"name": "X"}) + call = client._request.call_args + assert call.args[0] == "patch" + + async def test_upsert_multiple_issues_post(self): + """_upsert_multiple() sends a POST to the UpsertMultiple action endpoint.""" + client = _make_client() + _seed_cache(client) + client._request.return_value = _resp(status=204) + await client._upsert_multiple( + "accounts", + "account", + [{"accountnumber": "A"}, {"accountnumber": "B"}], + [{"name": "X"}, {"name": "Y"}], + ) + call = client._request.call_args + assert call.args[0] == "post" + assert "UpsertMultiple" in call.args[1] + + async def test_upsert_multiple_mismatched_length_raises(self): + """ValueError is raised when the alternate-key list and record list differ in length.""" + client = _make_client() + with pytest.raises(ValueError, match="same length"): + await client._upsert_multiple("accounts", "account", [{"k": "1"}], [{"n": "A"}, {"n": "B"}]) + + async def test_upsert_multiple_key_conflict_raises(self): + """ValueError is raised when a record field conflicts with its alternate-key field.""" + client = _make_client() + _seed_cache(client) + with pytest.raises(ValueError, match="conflicts"): + await client._upsert_multiple( + "accounts", + "account", + [{"accountnumber": "A"}], + [{"accountnumber": "B"}], + ) + + +# --------------------------------------------------------------------------- +# _bulk_fetch_picklists() / _convert_labels_to_ints() +# --------------------------------------------------------------------------- + + +class TestPicklists: + """Tests for _bulk_fetch_picklists() cache population and _convert_labels_to_ints() resolution.""" + + async def test_bulk_fetch_populates_cache(self): + """Picklist options are fetched from the API and stored with lowercased label keys.""" + client = _make_client() + body = { + "value": [ + { + "LogicalName": "statecode", + "OptionSet": { + "Options": [ + {"Value": 0, "Label": {"LocalizedLabels": [{"Label": "Active", "LanguageCode": 1033}]}}, + {"Value": 1, "Label": {"LocalizedLabels": [{"Label": "Inactive", "LanguageCode": 1033}]}}, + ] + }, + } + ] + } + client._request.return_value = _resp(json_data=body, status=200) + await client._bulk_fetch_picklists("account") + key = client._normalize_cache_key("account") + assert key in client._picklist_label_cache + picklists = client._picklist_label_cache[key]["picklists"] + assert "statecode" in picklists + assert picklists["statecode"]["active"] == 0 + + async def test_bulk_fetch_skips_on_cache_hit(self): + """A valid cache entry prevents an API call when the TTL has not expired.""" + client = _make_client() + key = client._normalize_cache_key("account") + client._picklist_label_cache[key] = {"ts": time.time(), "picklists": {}} + await client._bulk_fetch_picklists("account") + client._request.assert_not_called() + + async def test_bulk_fetch_empty_option_set(self): + """An attribute with an empty OptionSet is stored as an empty mapping.""" + client = _make_client() + body = {"value": [{"LogicalName": "field", "OptionSet": {}}]} + client._request.return_value = _resp(json_data=body, status=200) + await client._bulk_fetch_picklists("account") + key = client._normalize_cache_key("account") + assert client._picklist_label_cache[key]["picklists"]["field"] == {} + + async def test_convert_no_string_values_returns_unchanged(self): + """A record with no string values is returned as-is without any API lookup.""" + client = _make_client() + record = {"statecode": 0, "count": 5} + result = await client._convert_labels_to_ints("account", record) + assert result == record + client._request.assert_not_called() + + async def test_convert_string_resolved_to_int(self): + """A known label string is resolved to its integer option value from the cache.""" + client = _make_client() + key = client._normalize_cache_key("account") + client._picklist_label_cache[key] = { + "ts": time.time(), + "picklists": {"statecode": {"active": 0, "inactive": 1}}, + } + result = await client._convert_labels_to_ints("account", {"statecode": "Active"}) + assert result["statecode"] == 0 + + async def test_convert_odata_key_skipped(self): + """OData annotation fields with labels that don't match any option are left unchanged.""" + client = _make_client() + key = client._normalize_cache_key("account") + client._picklist_label_cache[key] = { + "ts": time.time(), + "picklists": {"@odata.type": {"val": 1}}, + } + record = {"@odata.type": "Microsoft.Dynamics.CRM.account"} + result = await client._convert_labels_to_ints("account", record) + assert result["@odata.type"] == "Microsoft.Dynamics.CRM.account" + + async def test_convert_unresolved_string_left_unchanged(self): + """A string value with no matching picklist entry is left as-is in the output.""" + client = _make_client() + key = client._normalize_cache_key("account") + client._picklist_label_cache[key] = {"ts": time.time(), "picklists": {}} + result = await client._convert_labels_to_ints("account", {"name": "Contoso"}) + assert result["name"] == "Contoso" + + +# --------------------------------------------------------------------------- +# _build_* async methods +# --------------------------------------------------------------------------- + + +class TestBuildMethods: + """Tests for _build_* async methods that produce _RawRequest objects without I/O.""" + + async def test_build_create_post_request(self): + """_build_create() produces a POST request targeting the entity set URL.""" + client = _make_client() + _seed_cache(client) + req = await client._build_create("accounts", "account", {"amount": 100}) + assert req.method == "POST" + assert "accounts" in req.url + + async def test_build_create_multiple_post_request(self): + """_build_create_multiple() produces a POST targeting the CreateMultiple action.""" + client = _make_client() + _seed_cache(client) + req = await client._build_create_multiple("accounts", "account", [{"amount": 100}]) + assert req.method == "POST" + assert "CreateMultiple" in req.url + + async def test_build_create_multiple_injects_odata_type(self): + """Each entry in the Targets list receives an @odata.type annotation.""" + client = _make_client() + _seed_cache(client) + req = await client._build_create_multiple("accounts", "account", [{"amount": 100}]) + body = json.loads(req.body) + assert "@odata.type" in body["Targets"][0] + + async def test_build_update_patch_request(self): + """_build_update() produces a PATCH request with an If-Match: * concurrency guard.""" + client = _make_client() + _seed_cache(client) + req = await client._build_update("account", "guid-1", {"name": "X"}) + assert req.method == "PATCH" + assert "accounts" in req.url + assert req.headers.get("If-Match") == "*" + + async def test_build_update_with_content_id_reference(self): + """A $-prefixed record_id is used as a raw changeset content-ID reference URL.""" + client = _make_client() + req = await client._build_update("account", "$1", {"name": "X"}) + assert req.url == "$1" + + async def test_build_delete_delete_request(self): + """_build_delete() produces a DELETE request with an If-Match: * concurrency guard.""" + client = _make_client() + _seed_cache(client) + req = await client._build_delete("account", "guid-1") + assert req.method == "DELETE" + assert "accounts" in req.url + assert req.headers.get("If-Match") == "*" + + async def test_build_delete_with_content_id_reference(self): + """A $-prefixed record_id is used as a raw changeset content-ID reference URL.""" + client = _make_client() + req = await client._build_delete("account", "$2") + assert req.url == "$2" + + async def test_build_get_get_request_with_select(self): + """_build_get() encodes the select list as a $select query string parameter.""" + client = _make_client() + _seed_cache(client) + req = await client._build_get("account", "guid-1", select=["name", "telephone1"]) + assert req.method == "GET" + assert "accounts" in req.url + assert "$select=name,telephone1" in req.url + + async def test_build_get_no_select(self): + """_build_get() omits $select from the URL when no columns are specified.""" + client = _make_client() + _seed_cache(client) + req = await client._build_get("account", "guid-1") + assert "$select" not in req.url + + async def test_build_sql_encodes_query(self): + """_build_sql() produces a GET request with the SQL statement in a sql= parameter.""" + client = _make_client() + _seed_cache(client) + req = await client._build_sql("SELECT name FROM account") + assert req.method == "GET" + assert "sql=" in req.url + assert "SELECT" in req.url or "SELECT" in req.url.replace("%20", " ") + + async def test_build_upsert_patch_request(self): + """_build_upsert() produces a PATCH without If-Match, allowing create-or-replace semantics.""" + client = _make_client() + req = await client._build_upsert("accounts", "account", {"accountnumber": "A"}, {"name": "X"}) + assert req.method == "PATCH" + assert "accounts" in req.url + assert req.headers is None or "If-Match" not in req.headers + + async def test_build_upsert_multiple_post_request(self): + """_build_upsert_multiple() produces a POST targeting the UpsertMultiple action.""" + client = _make_client() + req = await client._build_upsert_multiple( + "accounts", + "account", + [{"accountnumber": "A"}], + [{"name": "X"}], + ) + assert req.method == "POST" + assert "UpsertMultiple" in req.url + + async def test_build_upsert_multiple_mismatched_raises(self): + """ValidationError is raised when the alternate-key and record lists differ in length.""" + client = _make_client() + with pytest.raises(ValidationError): + await client._build_upsert_multiple("accounts", "account", [{"k": "1"}], [{"n": "A"}, {"n": "B"}]) + + async def test_build_upsert_multiple_key_conflict_raises(self): + """ValidationError is raised when a record field overwrites an alternate-key field.""" + client = _make_client() + with pytest.raises(ValidationError, match="conflicts"): + await client._build_upsert_multiple( + "accounts", + "account", + [{"accountnumber": "A"}], + [{"accountnumber": "B"}], + ) + + async def test_build_delete_multiple_bulk_delete(self): + """_build_delete_multiple() produces a POST BulkDelete request with a QuerySet body.""" + client = _make_client() + _seed_cache(client) + req = await client._build_delete_multiple("account", ["id-1", "id-2"]) + assert req.method == "POST" + assert "BulkDelete" in req.url + body = json.loads(req.body) + assert "QuerySet" in body + + async def test_build_update_multiple_broadcast(self): + """A dict for changes is broadcast to all IDs; Targets list length matches ID count.""" + client = _make_client() + _seed_cache(client) + req = await client._build_update_multiple("accounts", "account", ["id-1", "id-2"], {"name": "X"}) + assert req.method == "POST" + assert "UpdateMultiple" in req.url + body = json.loads(req.body) + assert len(body["Targets"]) == 2 + + async def test_build_update_multiple_paired(self): + """A list for changes is applied pairwise; Targets list matches the paired length.""" + client = _make_client() + _seed_cache(client) + req = await client._build_update_multiple("accounts", "account", ["id-1"], [{"name": "X"}]) + assert req.method == "POST" + body = json.loads(req.body) + assert len(body["Targets"]) == 1 + + async def test_build_update_multiple_invalid_changes_type_raises(self): + """ValidationError is raised when changes is neither a dict nor a list.""" + client = _make_client() + _seed_cache(client) + with pytest.raises(ValidationError): + await client._build_update_multiple("accounts", "account", ["id-1"], "invalid") + + async def test_build_update_multiple_mismatched_length_raises(self): + """ValidationError is raised when the ID list and changes list differ in length.""" + client = _make_client() + _seed_cache(client) + with pytest.raises(ValidationError): + await client._build_update_multiple("accounts", "account", ["id-1", "id-2"], [{"name": "X"}]) + + +# --------------------------------------------------------------------------- +# _wait_for_attribute_visibility() +# --------------------------------------------------------------------------- + + +class TestWaitForAttributeVisibility: + """Tests for _wait_for_attribute_visibility() polling loop.""" + + async def test_succeeds_on_first_attempt(self): + """Returns immediately when the first probe request succeeds.""" + client = _make_client() + client._request.return_value = _resp(status=200) + with patch("PowerPlatform.Dataverse.aio.data._async_odata.asyncio.sleep", new_callable=AsyncMock): + await client._wait_for_attribute_visibility("accounts", "new_notes", delays=(0,)) + client._request.assert_called_once() + + async def test_raises_after_all_delays_exhausted(self): + """RuntimeError is raised when every probe attempt fails and delays are exhausted.""" + client = _make_client() + client._request.side_effect = Exception("not visible") + with patch("PowerPlatform.Dataverse.aio.data._async_odata.asyncio.sleep", new_callable=AsyncMock): + with pytest.raises(RuntimeError, match="did not become visible"): + await client._wait_for_attribute_visibility("accounts", "new_notes", delays=(0, 0)) + + async def test_succeeds_after_retry(self): + """A transient failure on the first probe does not prevent success on the second.""" + client = _make_client() + client._request.side_effect = [Exception("not ready"), _resp(status=200)] + with patch("PowerPlatform.Dataverse.aio.data._async_odata.asyncio.sleep", new_callable=AsyncMock): + await client._wait_for_attribute_visibility("accounts", "new_notes", delays=(0, 0)) + assert client._request.call_count == 2 + + +# --------------------------------------------------------------------------- +# _request_metadata_with_retry() +# --------------------------------------------------------------------------- + + +class TestRequestMetadataWithRetry: + """Tests for _request_metadata_with_retry() which retries on transient 404 responses.""" + + async def test_success_on_first_attempt(self): + """A successful response is returned immediately without any retry.""" + client = _make_client() + client._request.return_value = _resp(status=200, json_data={"value": []}) + result = await client._request_metadata_with_retry("get", "https://example/url") + assert result.status == 200 + + async def test_non_404_raises_immediately(self): + """A non-404 HttpError is re-raised without retrying.""" + client = _make_client() + err = HttpError("Server error", status_code=500) + client._request.side_effect = err + with pytest.raises(HttpError): + await client._request_metadata_with_retry("get", "https://example/url") + assert client._request.call_count == 1 + + async def test_404_retries_and_raises_runtime_error(self): + """A 404 is retried max_attempts=5 times before RuntimeError is raised.""" + client = _make_client() + err = HttpError("Not found", status_code=404) + client._request.side_effect = err + with patch("PowerPlatform.Dataverse.aio.data._async_odata.asyncio.sleep", new_callable=AsyncMock): + with pytest.raises(RuntimeError, match="Metadata request failed"): + await client._request_metadata_with_retry("get", "https://example/url") + assert client._request.call_count == 5 # max_attempts defined in implementation + + +# --------------------------------------------------------------------------- +# Additional coverage tests +# --------------------------------------------------------------------------- + + +class TestRequestMergeAndEdgeCases: + """Coverage for _request() header-merge, text-decode failure, and Retry-After edge cases.""" + + def _auth_client(self): + auth = MagicMock() + auth._acquire_token = AsyncMock(return_value=MagicMock(access_token="token")) + return _AsyncODataClient(auth, "https://example.crm.dynamics.com") + + async def test_caller_headers_merged_with_base_headers(self): + """Headers passed by the caller are merged on top of base headers.""" + client = self._auth_client() + client._raw_request = AsyncMock(return_value=_resp(status=200, json_data={})) + await client._request( + "get", "https://example.crm.dynamics.com/api/data/v9.2/accounts", headers={"X-Custom": "value"} + ) + _, kwargs = client._raw_request.call_args + assert kwargs.get("headers", {}).get("X-Custom") == "value" + assert "Authorization" in kwargs.get("headers", {}) + + async def test_non_json_body_still_raises_http_error(self): + """When r.text is non-JSON, _request still raises HttpError with the status code.""" + client = self._auth_client() + r = MagicMock() + r.status = 400 + r.headers = {} + r.text = "not valid json \xff" + client._raw_request = AsyncMock(return_value=r) + with pytest.raises(HttpError) as exc: + await client._request("get", "https://example.crm.dynamics.com/api/data/v9.2/accounts") + assert exc.value.status_code == 400 + + async def test_retry_after_non_integer_handled(self): + """A non-integer Retry-After header (e.g. HTTP-date) does not crash _request.""" + client = self._auth_client() + body = {"error": {"code": "429", "message": "Too many requests"}} + r = _resp(status=429, json_data=body, headers={"Retry-After": "Wed, 21 Oct 2025 07:28:00 GMT"}) + client._raw_request = AsyncMock(return_value=r) + with pytest.raises(HttpError) as exc: + await client._request("get", "https://example.crm.dynamics.com/api/data/v9.2/accounts") + assert exc.value.to_dict()["details"].get("retry_after") is None + + +class TestCreateMultipleEdgeCases: + """Coverage for _create_multiple() JSON-parse and non-dict body paths.""" + + async def test_json_parse_failure_returns_empty_list(self): + """When response JSON cannot be parsed, returns empty list without raising.""" + client = _make_client() + _seed_cache(client) + r = MagicMock() + r.status = 200 + r.headers = {} + r.json = MagicMock(side_effect=ValueError("not json")) + client._request.return_value = r + result = await client._create_multiple("accounts", "account", [{"amount": 1}]) + assert result == [] + + async def test_non_dict_body_returns_empty_list(self): + """When response body is a list (not dict), returns empty list.""" + client = _make_client() + _seed_cache(client) + client._request.return_value = _resp(json_data=[1, 2, 3], status=200) + result = await client._create_multiple("accounts", "account", [{"amount": 1}]) + assert result == [] + + +class TestGetMultipleEdgeCases: + """Coverage for _get_multiple() JSON-parse failure path.""" + + async def test_json_parse_failure_returns_empty_page(self): + """When _do_request JSON parse fails, an empty dict is returned (no crash).""" + client = _make_client() + _seed_cache(client) + r = MagicMock() + r.status = 200 + r.headers = {} + r.json = MagicMock(side_effect=ValueError("not json")) + client._request.return_value = r + pages = [page async for page in client._get_multiple("account")] + assert pages == [] + + +class TestQuerySqlEdgeCases: + """Coverage for _query_sql() JSON-parse, non-dict body, and pagination error paths.""" + + async def test_json_parse_failure_returns_empty_list(self): + """When JSON parse fails on first response, returns empty list.""" + client = _make_client() + _seed_cache(client) + r = MagicMock() + r.status = 200 + r.headers = {} + r.json = MagicMock(side_effect=ValueError("not json")) + client._request.return_value = r + result = await client._query_sql("SELECT name FROM account") + assert result == [] + + async def test_non_dict_body_returns_empty_list(self): + """When response body is not a dict (e.g. bare list of non-dicts), returns [].""" + client = _make_client() + _seed_cache(client) + client._request.return_value = _resp(json_data="not-a-dict", status=200) + result = await client._query_sql("SELECT name FROM account") + assert result == [] + + async def test_pagination_duplicate_cookie_warns_and_stops(self): + """Duplicate pagingcookie in $skiptoken triggers RuntimeWarning and stops pagination.""" + import warnings + from urllib.parse import quote + + client = _make_client() + _seed_cache(client) + # Build two skiptokens with the same pagingcookie value but different pagenumbers. + # _extract_pagingcookie extracts the pagingcookie= attribute value; if it's the + # same in both pages, the duplicate-cookie guard fires. + cookie_val = "%3Ccookie+guid%3D%22abc%22%3E" # same encoded cookie in both pages + outer1 = f'' + outer2 = f'' + next_link1 = f"https://example.crm.dynamics.com/api/data/v9.2/accounts?$skiptoken={quote(outer1)}" + next_link2 = f"https://example.crm.dynamics.com/api/data/v9.2/accounts?$skiptoken={quote(outer2)}" + page1 = _resp( + json_data={ + "value": [{"name": "A", "accountid": "g1"}], + "@odata.nextLink": next_link1, + }, + status=200, + ) + page2 = _resp( + json_data={ + "value": [{"name": "B", "accountid": "g2"}], + "@odata.nextLink": next_link2, + }, + status=200, + ) + client._request.side_effect = [page1, page2] + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + result = await client._query_sql("SELECT name FROM account") + assert any("pagingcookie" in str(warning.message) for warning in w) + assert len(result) >= 1 + + async def test_pagination_next_page_request_fails_warns_and_stops(self): + """When the next-page request raises, a RuntimeWarning is emitted and pagination stops.""" + import warnings + + client = _make_client() + _seed_cache(client) + next_link = "https://example.crm.dynamics.com/api/data/v9.2/accounts?$skiptoken=abc" + page1 = _resp( + json_data={ + "value": [{"name": "A", "accountid": "g1"}], + "@odata.nextLink": next_link, + }, + status=200, + ) + client._request.side_effect = [page1, HttpError("server error", status_code=500)] + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + result = await client._query_sql("SELECT name FROM account") + assert any("next-page request failed" in str(warning.message) for warning in w) + assert result == [{"name": "A", "accountid": "g1"}] + + async def test_pagination_next_page_non_json_warns_and_stops(self): + """When the next-page response is not JSON, a RuntimeWarning is emitted.""" + import warnings + + client = _make_client() + _seed_cache(client) + next_link = "https://example.crm.dynamics.com/api/data/v9.2/accounts?$skiptoken=abc" + page1 = _resp( + json_data={ + "value": [{"name": "A", "accountid": "g1"}], + "@odata.nextLink": next_link, + }, + status=200, + ) + bad_resp = MagicMock() + bad_resp.status = 200 + bad_resp.headers = {} + bad_resp.json = MagicMock(side_effect=ValueError("not json")) + client._request.side_effect = [page1, bad_resp] + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + result = await client._query_sql("SELECT name FROM account") + assert any("not valid JSON" in str(warning.message) for warning in w) + assert result == [{"name": "A", "accountid": "g1"}] + + async def test_pagination_non_dict_page_body_stops(self): + """When a paginated response body is not a dict, pagination stops cleanly.""" + client = _make_client() + _seed_cache(client) + next_link = "https://example.crm.dynamics.com/api/data/v9.2/accounts?$skiptoken=abc" + page1 = _resp( + json_data={ + "value": [{"name": "A", "accountid": "g1"}], + "@odata.nextLink": next_link, + }, + status=200, + ) + page2 = _resp(json_data="not-a-dict", status=200) + client._request.side_effect = [page1, page2] + result = await client._query_sql("SELECT name FROM account") + assert result == [{"name": "A", "accountid": "g1"}] + + +class TestPrimaryIdAttrEdgeCases: + """Coverage for _primary_id_attr() RuntimeError when metadata lacks PrimaryIdAttribute.""" + + async def test_raises_when_pk_not_in_cache_after_metadata_fetch(self): + """RuntimeError raised when entity resolves but PrimaryIdAttribute is absent from cache.""" + client = _make_client() + # Populate entity set cache but NOT the primaryid cache + key = client._normalize_cache_key("account") + client._logical_to_entityset_cache[key] = "accounts" + # _entity_set_from_schema_name will hit the cache and return without populating primaryid + with pytest.raises(RuntimeError, match="PrimaryIdAttribute not resolved"): + await client._primary_id_attr("account") + + +class TestGetAttributeMetadataEdgeCases: + """Coverage for _get_attribute_metadata() skip and JSON-parse-failure paths.""" + + async def test_skips_at_sign_fields_in_extra_select(self): + """Fields starting with '@' in extra_select are silently ignored.""" + client = _make_client() + client._request.return_value = _resp( + json_data={"value": [{"MetadataId": "m1", "LogicalName": "name", "SchemaName": "Name"}]}, + status=200, + ) + result = await client._get_attribute_metadata("meta-1", "name", extra_select="@odata.type,AttributeType") + assert result is not None + assert result["LogicalName"] == "name" + + async def test_json_parse_failure_returns_none(self): + """When response JSON parse fails, None is returned without raising.""" + client = _make_client() + r = MagicMock() + r.status = 200 + r.headers = {} + r.json = MagicMock(side_effect=ValueError("not json")) + client._request.return_value = r + result = await client._get_attribute_metadata("meta-1", "name") + assert result is None + + +class TestPicklistEdgeCases: + """Coverage for _bulk_fetch_picklists() guard clauses and _convert_labels_to_ints() paths.""" + + async def test_bulk_fetch_cache_hit_inside_lock_skips_fetch(self): + """Second TTL check inside the lock exits early when another coroutine populated cache.""" + import time + + client = _make_client() + key = client._normalize_cache_key("account") + # Pre-populate cache with a fresh entry (TTL not expired) + client._picklist_label_cache[key] = {"ts": time.time(), "picklists": {}} + # Warm the outer cache check too + client._picklist_label_cache[key]["ts"] = time.time() + # Should return without calling _request + await client._bulk_fetch_picklists("account") + client._request_metadata_with_retry = AsyncMock() + client._request_metadata_with_retry.assert_not_called() + + async def test_bulk_fetch_skips_non_dict_items(self): + """Non-dict items in the picklist response value list are skipped.""" + client = _make_client() + r = _resp(json_data={"value": ["not-a-dict", {"LogicalName": "status", "OptionSet": {"Options": []}}]}) + client._request_metadata_with_retry = AsyncMock(return_value=r) + await client._bulk_fetch_picklists("account") # should not raise + + async def test_bulk_fetch_skips_empty_logical_name(self): + """Items with empty LogicalName are skipped during picklist fetch.""" + client = _make_client() + r = _resp(json_data={"value": [{"LogicalName": "", "OptionSet": {"Options": []}}]}) + client._request_metadata_with_retry = AsyncMock(return_value=r) + await client._bulk_fetch_picklists("account") # should not raise + + async def test_bulk_fetch_skips_non_dict_options(self): + """Non-dict entries in OptionSet.Options are skipped.""" + client = _make_client() + r = _resp(json_data={"value": [{"LogicalName": "status", "OptionSet": {"Options": ["bad"]}}]}) + client._request_metadata_with_retry = AsyncMock(return_value=r) + await client._bulk_fetch_picklists("account") # should not raise + + async def test_bulk_fetch_skips_non_int_value(self): + """Options whose Value is not an int are skipped.""" + client = _make_client() + r = _resp( + json_data={ + "value": [ + { + "LogicalName": "status", + "OptionSet": {"Options": [{"Value": "not-an-int", "Label": {}}]}, + } + ] + } + ) + client._request_metadata_with_retry = AsyncMock(return_value=r) + await client._bulk_fetch_picklists("account") # should not raise + + async def test_convert_labels_non_dict_cache_entry_returns_record(self): + """When picklist cache entry is not a dict, record is returned unchanged.""" + client = _make_client() + key = client._normalize_cache_key("account") + client._picklist_label_cache[key] = "not-a-dict" + result = await client._convert_labels_to_ints("account", {"status": "Active"}) + assert result == {"status": "Active"} + + async def test_convert_labels_skips_odata_annotation_keys(self): + """Keys containing '@odata.' are not looked up in the picklist cache.""" + import time + + client = _make_client() + key = client._normalize_cache_key("account") + client._picklist_label_cache[key] = { + "ts": time.time(), + "picklists": {"status": {"active": 1}}, + } + record = {"status": "active", "status@odata.type": "#Microsoft.Dynamics.CRM.StatusType"} + result = await client._convert_labels_to_ints("account", record) + # status resolved; odata annotation key left untouched + assert result["status"] == 1 + assert "status@odata.type" in result + + +class TestCreateEntityEdgeCases: + """Coverage for _create_entity() solution_name, missing EntitySetName, missing MetadataId.""" + + async def test_create_entity_with_solution_unique_name(self): + """solution_unique_name is passed as a query parameter to the POST request.""" + client = _make_client() + client._request.return_value = _resp(status=204) + entity_resp = { + "LogicalName": "new_table", + "EntitySetName": "new_tables", + "MetadataId": "meta-999", + "SchemaName": "new_table", + "PrimaryIdAttribute": "new_tableid", + } + client._get_entity_by_table_schema_name = AsyncMock(return_value=entity_resp) + result = await client._create_entity( + "new_table", + "New Table", + [], + solution_unique_name="MySolution", + ) + _, kwargs = client._request.call_args + assert kwargs.get("params", {}).get("SolutionUniqueName") == "MySolution" + assert result["EntitySetName"] == "new_tables" + + async def test_create_entity_missing_entity_set_name_raises(self): + """RuntimeError raised when EntitySetName is absent after create.""" + client = _make_client() + client._request.return_value = _resp(status=204) + client._get_entity_by_table_schema_name = AsyncMock(return_value={"MetadataId": "m1"}) + with pytest.raises(RuntimeError, match="EntitySetName not available"): + await client._create_entity("t", "t", "T", []) + + async def test_create_entity_missing_metadata_id_raises(self): + """RuntimeError raised when MetadataId is absent after create.""" + client = _make_client() + client._request.return_value = _resp(status=204) + client._get_entity_by_table_schema_name = AsyncMock(return_value={"EntitySetName": "ts", "LogicalName": "t"}) + with pytest.raises(RuntimeError, match="MetadataId missing"): + await client._create_entity("t", "t", "T", []) + + +class TestWaitForAttributeVisibilityWithDelay: + """Coverage for _wait_for_attribute_visibility() sleep branch.""" + + async def test_waits_when_delay_is_nonzero(self): + """asyncio.sleep is called when the computed delay is positive.""" + client = _make_client() + # First call (delay=0) fails so the loop continues to delay=1 where sleep fires. + ok = _resp(status=200) + client._request.side_effect = [HttpError("not yet", status_code=404), ok] + with patch("PowerPlatform.Dataverse.aio.data._async_odata.asyncio.sleep", new_callable=AsyncMock) as mock_sleep: + await client._wait_for_attribute_visibility("accounts", "new_col", delays=(0, 1)) + mock_sleep.assert_called_once_with(1) + + +class TestAlternateKeyWithDisplayName: + """Coverage for _create_alternate_key() display_name_label path.""" + + async def test_create_alternate_key_with_display_name(self): + """DisplayName payload key is set when display_name_label is provided.""" + client = _make_client() + ent = {"LogicalName": "account", "EntitySetName": "accounts", "MetadataId": "m1", "SchemaName": "Account"} + client._get_entity_by_table_schema_name = AsyncMock(return_value=ent) + r = _resp(status=204, headers={"OData-EntityId": "https://example.com/(key123)"}) + r.headers = {"OData-EntityId": "https://example.com/(key123)"} + client._request.return_value = r + + label = MagicMock() + label.to_dict.return_value = {"UserLocalizedLabel": {"Label": "Account Number", "LanguageCode": 1033}} + + result = await client._create_alternate_key("account", "AccountNumber_AK", ["accountnumber"], label) + assert result["schema_name"] == "AccountNumber_AK" + _, kwargs = client._request.call_args + assert "DisplayName" in kwargs.get("json", {}) + + +class TestBuildMethodsAdditional: + """Coverage for _build_create_multiple TypeError, _build_get annotations, and _build_list.""" + + async def test_build_create_multiple_non_dict_raises_type_error(self): + """_build_create_multiple() raises TypeError when records contain non-dicts.""" + client = _make_client() + _seed_cache(client) + with pytest.raises(TypeError, match="dicts"): + await client._build_create_multiple("accounts", "account", ["not-a-dict"]) + + async def test_build_get_with_include_annotations(self): + """_build_get() sets Prefer header when include_annotations is specified.""" + client = _make_client() + _seed_cache(client) + req = await client._build_get("account", "guid-1", include_annotations="*") + assert req.headers is not None + assert "odata.include-annotations" in req.headers.get("Prefer", "") + + async def test_build_list_basic(self): + """_build_list() produces a GET request targeting the entity-set URL.""" + client = _make_client() + _seed_cache(client) + req = await client._build_list("account") + assert req.method == "GET" + assert "accounts" in req.url + assert req.headers is None + + async def test_build_list_with_select_filter_orderby_top(self): + """_build_list() encodes all OData query parameters into the URL.""" + client = _make_client() + _seed_cache(client) + req = await client._build_list( + "account", + select=["name", "telephone1"], + filter="statecode eq 0", + orderby=["name asc"], + top=10, + ) + assert "$select=name,telephone1" in req.url + assert "$filter=statecode+eq+0" in req.url or "$filter=statecode%20eq%200" in req.url or "statecode" in req.url + assert "$top=10" in req.url + + async def test_build_list_with_page_size_and_annotations(self): + """_build_list() sets Prefer header for page_size and include_annotations.""" + client = _make_client() + _seed_cache(client) + req = await client._build_list("account", page_size=50, include_annotations="*") + assert req.headers is not None + prefer = req.headers.get("Prefer", "") + assert "odata.maxpagesize=50" in prefer + assert "odata.include-annotations" in prefer + + async def test_build_list_with_count(self): + """_build_list() appends $count=true when count=True.""" + client = _make_client() + _seed_cache(client) + req = await client._build_list("account", count=True) + assert "$count=true" in req.url + + +class TestAsyncOperationContextUserAgent: + """User-Agent header reflects operation_context on the async client.""" + + async def test_default_user_agent_unchanged(self): + from PowerPlatform.Dataverse.aio.data._async_odata import _USER_AGENT + + client = _make_client() + headers = await client._headers() + assert headers["User-Agent"] == _USER_AGENT + + async def test_operation_context_appended(self): + from PowerPlatform.Dataverse.aio.data._async_odata import _USER_AGENT + from PowerPlatform.Dataverse.core.config import DataverseConfig, OperationContext + + ctx_str = "app=dataverse-skills/1.2.1;agent=claude-code" + config = DataverseConfig(operation_context=OperationContext(user_agent_context=ctx_str)) + auth = MagicMock() + auth._acquire_token = AsyncMock(return_value=MagicMock(access_token="test-token")) + client = _AsyncODataClient(auth, "https://example.crm.dynamics.com", config=config) + headers = await client._headers() + assert headers["User-Agent"] == f"{_USER_AGENT} ({ctx_str})" + + async def test_none_context_no_parentheses(self): + from PowerPlatform.Dataverse.core.config import DataverseConfig + + config = DataverseConfig(operation_context=None) + auth = MagicMock() + auth._acquire_token = AsyncMock(return_value=MagicMock(access_token="test-token")) + client = _AsyncODataClient(auth, "https://example.crm.dynamics.com", config=config) + headers = await client._headers() + assert "(" not in headers["User-Agent"] diff --git a/tests/unit/aio/data/test_async_relationships.py b/tests/unit/aio/data/test_async_relationships.py new file mode 100644 index 00000000..cf0ed1bd --- /dev/null +++ b/tests/unit/aio/data/test_async_relationships.py @@ -0,0 +1,314 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +"""Unit tests for _AsyncRelationshipOperationsMixin.""" + +from unittest.mock import AsyncMock, MagicMock + +import pytest + +from PowerPlatform.Dataverse.aio.data._async_odata import _AsyncODataClient +from PowerPlatform.Dataverse.core.errors import MetadataError +from PowerPlatform.Dataverse.models.relationship import ( + LookupAttributeMetadata, + ManyToManyRelationshipMetadata, + OneToManyRelationshipMetadata, +) +from PowerPlatform.Dataverse.models.labels import Label, LocalizedLabel + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + + +def _make_client() -> _AsyncODataClient: + """Return _AsyncODataClient with _request mocked at the HTTP boundary.""" + auth = MagicMock() + auth._acquire_token = AsyncMock(return_value=MagicMock(access_token="token")) + client = _AsyncODataClient(auth, "https://example.crm.dynamics.com") + client._request = AsyncMock() + return client + + +def _resp(json_data=None, status=200, headers=None): + """Create a mock _AsyncResponse-compatible response.""" + r = MagicMock() + r.status = status + r.headers = headers or {} + r.json = MagicMock(return_value=json_data if json_data is not None else {}) + return r + + +def _entity_def(meta_id="meta-001", logical="account"): + """Return a minimal EntityDefinitions value-list response body.""" + return { + "value": [ + { + "LogicalName": logical, + "EntitySetName": "accounts", + "PrimaryIdAttribute": "accountid", + "MetadataId": meta_id, + "SchemaName": "Account", + } + ] + } + + +def _label(text: str = "Test") -> Label: + """Return a Label with a single English localized label.""" + return Label(localized_labels=[LocalizedLabel(label=text, language_code=1033)]) + + +def _seed_cache(client, table="account", entity_set="accounts", pk="accountid"): + """Pre-populate entity-set and primary-ID caches to bypass HTTP for schema-name lookups.""" + key = client._normalize_cache_key(table) + client._logical_to_entityset_cache[key] = entity_set + client._logical_primaryid_cache[key] = pk + + +# --------------------------------------------------------------------------- +# _extract_id_from_header (sync) +# --------------------------------------------------------------------------- + + +class TestExtractIdFromHeader: + """Tests for _extract_id_from_header(), which parses GUIDs from OData-EntityId URLs. + + The regex matches only hex characters and dashes inside parentheses, so + only proper UUID-format strings are extracted. + """ + + def test_extracts_guid_from_url(self): + """A UUID enclosed in parentheses at the end of a URL is returned.""" + client = _make_client() + guid = "a1b2c3d4-e5f6-7890-abcd-ef1234567890" + header = f"https://example.crm.dynamics.com/api/data/v9.2/RelationshipDefinitions({guid})" + result = client._extract_id_from_header(header) + assert result == guid + + def test_returns_none_for_empty_header(self): + """None is returned for both None and empty-string inputs.""" + client = _make_client() + assert client._extract_id_from_header(None) is None + assert client._extract_id_from_header("") is None + + def test_returns_none_when_no_guid(self): + """None is returned when the header contains no hex UUID in parentheses.""" + client = _make_client() + assert client._extract_id_from_header("no-guid-here") is None + + +# --------------------------------------------------------------------------- +# _create_one_to_many_relationship() +# --------------------------------------------------------------------------- + + +class TestCreateOneToManyRelationship: + """Tests for _create_one_to_many_relationship() one-to-many relationship creation.""" + + async def test_success(self): + """The relationship ID, schema name, and lookup schema name are returned on success.""" + client = _make_client() + guid = "a1b2c3d4-e5f6-7890-abcd-ef1234567890" + client._request.return_value = _resp( + status=204, + headers={ + "OData-EntityId": f"https://example.crm.dynamics.com/api/data/v9.2/RelationshipDefinitions({guid})" + }, + ) + lookup = LookupAttributeMetadata(schema_name="new_DeptId", display_name=_label("Dept")) + relationship = OneToManyRelationshipMetadata( + schema_name="new_Dept_Emp", + referenced_entity="new_dept", + referencing_entity="new_employee", + referenced_attribute="new_deptid", + ) + result = await client._create_one_to_many_relationship(lookup, relationship) + assert result["relationship_id"] == guid + assert result["relationship_schema_name"] == "new_Dept_Emp" + assert result["lookup_schema_name"] == "new_DeptId" + + async def test_with_solution(self): + """The MSCRM.SolutionUniqueName header is injected when a solution name is supplied.""" + client = _make_client() + client._request.return_value = _resp(status=204, headers={}) + lookup = LookupAttributeMetadata(schema_name="new_DeptId", display_name=_label("Dept")) + relationship = OneToManyRelationshipMetadata( + schema_name="new_Dept_Emp", + referenced_entity="new_dept", + referencing_entity="new_employee", + referenced_attribute="new_deptid", + ) + await client._create_one_to_many_relationship(lookup, relationship, solution="MySolution") + call_kwargs = client._request.call_args.kwargs + headers = call_kwargs.get("headers", {}) + assert "MSCRM.SolutionUniqueName" in headers + assert headers["MSCRM.SolutionUniqueName"] == "MySolution" + + +# --------------------------------------------------------------------------- +# _create_many_to_many_relationship() +# --------------------------------------------------------------------------- + + +class TestCreateManyToManyRelationship: + """Tests for _create_many_to_many_relationship() many-to-many relationship creation.""" + + async def test_success(self): + """The relationship ID and entity names are returned on success.""" + client = _make_client() + guid = "b2c3d4e5-f6a7-8901-bcde-f12345678901" + client._request.return_value = _resp( + status=204, + headers={ + "OData-EntityId": f"https://example.crm.dynamics.com/api/data/v9.2/RelationshipDefinitions({guid})" + }, + ) + relationship = ManyToManyRelationshipMetadata( + schema_name="new_emp_proj", + entity1_logical_name="new_employee", + entity2_logical_name="new_project", + ) + result = await client._create_many_to_many_relationship(relationship) + assert result["relationship_id"] == guid + assert result["relationship_schema_name"] == "new_emp_proj" + assert result["entity1_logical_name"] == "new_employee" + assert result["entity2_logical_name"] == "new_project" + + async def test_with_solution(self): + """The MSCRM.SolutionUniqueName header is injected when a solution name is supplied.""" + client = _make_client() + client._request.return_value = _resp(status=204, headers={}) + relationship = ManyToManyRelationshipMetadata( + schema_name="new_emp_proj", + entity1_logical_name="new_employee", + entity2_logical_name="new_project", + ) + await client._create_many_to_many_relationship(relationship, solution="MySol") + headers = client._request.call_args.kwargs.get("headers", {}) + assert headers.get("MSCRM.SolutionUniqueName") == "MySol" + + +# --------------------------------------------------------------------------- +# _delete_relationship() +# --------------------------------------------------------------------------- + + +class TestDeleteRelationship: + """Tests for _delete_relationship() relationship removal by GUID.""" + + async def test_issues_delete(self): + """A DELETE request is issued containing the relationship GUID in the URL.""" + client = _make_client() + client._request.return_value = _resp(status=204) + await client._delete_relationship("rel-guid-1") + call_args = client._request.call_args + assert call_args.args[0] == "delete" + assert "rel-guid-1" in call_args.args[1] + + async def test_sets_if_match_header(self): + """An If-Match: * header is sent to prevent accidental deletion of a stale version.""" + client = _make_client() + client._request.return_value = _resp(status=204) + await client._delete_relationship("rel-guid-1") + headers = client._request.call_args.kwargs.get("headers", {}) + assert headers.get("If-Match") == "*" + + +# --------------------------------------------------------------------------- +# _get_relationship() +# --------------------------------------------------------------------------- + + +class TestGetRelationship: + """Tests for _get_relationship() single-relationship lookup by schema name.""" + + async def test_returns_relationship_dict(self): + """The first matching relationship dict from the value list is returned.""" + client = _make_client() + rel = {"SchemaName": "new_Dept_Emp", "RelationshipId": "rel-1"} + client._request.return_value = _resp(json_data={"value": [rel]}) + result = await client._get_relationship("new_Dept_Emp") + assert result == rel + + async def test_returns_none_when_not_found(self): + """None is returned when the API returns an empty value list.""" + client = _make_client() + client._request.return_value = _resp(json_data={"value": []}) + result = await client._get_relationship("nonexistent") + assert result is None + + +# --------------------------------------------------------------------------- +# _list_relationships() +# --------------------------------------------------------------------------- + + +class TestListRelationships: + """Tests for _list_relationships() global relationship listing.""" + + async def test_returns_all_relationships(self): + """The full value list is returned when no filter is applied.""" + client = _make_client() + rels = [{"SchemaName": "rel1"}, {"SchemaName": "rel2"}] + client._request.return_value = _resp(json_data={"value": rels}) + result = await client._list_relationships() + assert result == rels + + async def test_with_filter_and_select(self): + """Optional filter and select parameters are forwarded as OData query params.""" + client = _make_client() + client._request.return_value = _resp(json_data={"value": []}) + result = await client._list_relationships( + filter="RelationshipType eq 'OneToMany'", + select=["SchemaName"], + ) + assert result == [] + call_kwargs = client._request.call_args.kwargs + params = call_kwargs.get("params", {}) + assert "$filter" in params + assert "$select" in params + + +# --------------------------------------------------------------------------- +# _list_table_relationships() +# --------------------------------------------------------------------------- + + +class TestListTableRelationships: + """Tests for _list_table_relationships() which aggregates all three relationship types.""" + + async def test_combines_three_relationship_types(self): + """One-to-many, many-to-one, and many-to-many relationships are combined into one list.""" + client = _make_client() + entity_resp = _resp(json_data=_entity_def()) + otm_resp = _resp(json_data={"value": [{"SchemaName": "rel_otm"}]}) + mto_resp = _resp(json_data={"value": [{"SchemaName": "rel_mto"}]}) + mtm_resp = _resp(json_data={"value": [{"SchemaName": "rel_mtm"}]}) + client._request.side_effect = [entity_resp, otm_resp, mto_resp, mtm_resp] + result = await client._list_table_relationships("account") + assert len(result) == 3 + schema_names = [r["SchemaName"] for r in result] + assert "rel_otm" in schema_names + assert "rel_mto" in schema_names + assert "rel_mtm" in schema_names + + async def test_table_not_found_raises(self): + """MetadataError is raised when the table does not exist in entity metadata.""" + client = _make_client() + client._request.return_value = _resp(json_data={"value": []}) + with pytest.raises(MetadataError, match="not found"): + await client._list_table_relationships("nonexistent") + + async def test_with_filter_and_select(self): + """Optional filter and select parameters are forwarded to all three relationship requests.""" + client = _make_client() + entity_resp = _resp(json_data=_entity_def()) + empty_resp = _resp(json_data={"value": []}) + client._request.side_effect = [entity_resp, empty_resp, empty_resp, empty_resp] + result = await client._list_table_relationships( + "account", + filter="IsCustomRelationship eq true", + select=["SchemaName"], + ) + assert result == [] diff --git a/tests/unit/aio/data/test_async_upload.py b/tests/unit/aio/data/test_async_upload.py new file mode 100644 index 00000000..d5268913 --- /dev/null +++ b/tests/unit/aio/data/test_async_upload.py @@ -0,0 +1,318 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +"""Unit tests for _AsyncFileUploadMixin.""" + +import os +import tempfile +from unittest.mock import AsyncMock, MagicMock, patch + +import pytest + +from PowerPlatform.Dataverse.aio.data._async_odata import _AsyncODataClient + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + + +def _make_client() -> _AsyncODataClient: + """Return _AsyncODataClient with _request mocked at the HTTP boundary.""" + auth = MagicMock() + auth._acquire_token = AsyncMock(return_value=MagicMock(access_token="token")) + client = _AsyncODataClient(auth, "https://example.crm.dynamics.com") + client._request = AsyncMock() + return client + + +def _resp(status=200, headers=None, json_data=None): + """Create a mock _AsyncResponse-compatible response.""" + r = MagicMock() + r.status = status + r.headers = headers or {} + r.json = MagicMock(return_value=json_data or {}) + return r + + +def _seed_cache(client, table="account", entity_set="accounts", pk="accountid"): + """Pre-populate entity-set and primary-ID caches to bypass HTTP for schema-name lookups.""" + key = client._normalize_cache_key(table) + client._logical_to_entityset_cache[key] = entity_set + client._logical_primaryid_cache[key] = pk + + +def _entity_def(meta_id="meta-001", entity_set="accounts", logical="account"): + """Return a minimal EntityDefinitions value-list response body.""" + return { + "value": [ + { + "LogicalName": logical, + "EntitySetName": entity_set, + "PrimaryIdAttribute": "accountid", + "MetadataId": meta_id, + "SchemaName": "Account", + } + ] + } + + +# --------------------------------------------------------------------------- +# _upload_file_small() +# --------------------------------------------------------------------------- + + +class TestUploadFileSmall: + """Tests for _upload_file_small(), the single-request upload path for small files.""" + + async def test_success_uploads_with_patch(self): + """A successful upload issues a PATCH with x-ms-file-name and If-None-Match headers.""" + client = _make_client() + client._request.return_value = _resp(status=204) + with tempfile.NamedTemporaryFile(delete=False, suffix=".txt") as f: + f.write(b"hello world") + path = f.name + try: + await client._upload_file_small("accounts", "guid-1", "new_document", path) + call_args = client._request.call_args + assert call_args.args[0] == "patch" + headers = call_args.kwargs.get("headers", {}) + assert "x-ms-file-name" in headers + assert headers.get("If-None-Match") == "null" + finally: + os.unlink(path) + + async def test_success_with_overwrite(self): + """When if_none_match=False, an If-Match: * header is sent instead of If-None-Match.""" + client = _make_client() + client._request.return_value = _resp(status=204) + with tempfile.NamedTemporaryFile(delete=False, suffix=".txt") as f: + f.write(b"hello world") + path = f.name + try: + await client._upload_file_small("accounts", "guid-1", "new_document", path, if_none_match=False) + headers = client._request.call_args.kwargs.get("headers", {}) + assert headers.get("If-Match") == "*" + assert "If-None-Match" not in headers + finally: + os.unlink(path) + + async def test_explicit_mime_type(self): + """An explicit content_type is forwarded as the Content-Type header.""" + client = _make_client() + client._request.return_value = _resp(status=204) + with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as f: + f.write(b"%PDF") + path = f.name + try: + await client._upload_file_small("accounts", "guid-1", "new_document", path, content_type="application/pdf") + headers = client._request.call_args.kwargs.get("headers", {}) + assert headers.get("Content-Type") == "application/pdf" + finally: + os.unlink(path) + + async def test_empty_record_id_raises(self): + """ValueError is raised immediately when record_id is an empty string.""" + client = _make_client() + with pytest.raises(ValueError, match="record_id required"): + await client._upload_file_small("accounts", "", "new_doc", "/any/path") + + async def test_file_not_found_raises(self): + """FileNotFoundError is raised when the specified file path does not exist.""" + client = _make_client() + with pytest.raises(FileNotFoundError): + await client._upload_file_small("accounts", "guid-1", "new_doc", "/nonexistent/path.txt") + + async def test_file_too_large_raises(self): + """ValueError is raised when the file size exceeds the single-upload size limit.""" + client = _make_client() + with tempfile.NamedTemporaryFile(delete=False, suffix=".txt") as f: + f.write(b"x") + path = f.name + try: + stat_result = MagicMock() + stat_result.st_size = 200 * 1024 * 1024 + stat_result.st_mode = 0o100644 # regular file + with patch("pathlib.Path.stat", return_value=stat_result): + with pytest.raises(ValueError, match="exceeds single-upload limit"): + await client._upload_file_small("accounts", "guid-1", "new_doc", path) + finally: + os.unlink(path) + + +# --------------------------------------------------------------------------- +# _upload_file_chunk() +# --------------------------------------------------------------------------- + + +class TestUploadFileChunk: + """Tests for _upload_file_chunk(), the chunked upload path for large files.""" + + async def test_success_single_chunk(self): + """A small file completes in two requests: session init and one chunk PUT.""" + client = _make_client() + location = "https://example.crm.dynamics.com/api/data/v9.2/accounts(guid-1)/new_document?sessiontoken=xyz" + init_resp = _resp(status=200, headers={"Location": location, "x-ms-chunk-size": "4194304"}) + chunk_resp = _resp(status=204) + client._request.side_effect = [init_resp, chunk_resp] + with tempfile.NamedTemporaryFile(delete=False, suffix=".txt") as f: + f.write(b"hello world") + path = f.name + try: + await client._upload_file_chunk("accounts", "guid-1", "new_document", path) + assert client._request.call_count == 2 + finally: + os.unlink(path) + + async def test_success_with_if_match(self): + """When if_none_match=False, an If-Match: * header is included in the session-init request.""" + client = _make_client() + location = "https://example.crm.dynamics.com/api/data/v9.2/accounts(guid-1)/new_document?sessiontoken=abc" + init_resp = _resp(status=200, headers={"Location": location}) + chunk_resp = _resp(status=204) + client._request.side_effect = [init_resp, chunk_resp] + with tempfile.NamedTemporaryFile(delete=False, suffix=".txt") as f: + f.write(b"data") + path = f.name + try: + await client._upload_file_chunk("accounts", "guid-1", "new_document", path, if_none_match=False) + init_headers = client._request.call_args_list[0].kwargs.get("headers", {}) + assert init_headers.get("If-Match") == "*" + finally: + os.unlink(path) + + async def test_empty_record_id_raises(self): + """ValueError is raised immediately when record_id is an empty string.""" + client = _make_client() + with pytest.raises(ValueError, match="record_id required"): + await client._upload_file_chunk("accounts", "", "new_doc", "/any/path") + + async def test_file_not_found_raises(self): + """FileNotFoundError is raised when the specified file path does not exist.""" + client = _make_client() + with pytest.raises(FileNotFoundError): + await client._upload_file_chunk("accounts", "guid-1", "new_doc", "/nonexistent/path.txt") + + async def test_missing_location_header_raises(self): + """RuntimeError is raised when the session-init response lacks a Location header.""" + client = _make_client() + client._request.return_value = _resp(status=200, headers={}) + with tempfile.NamedTemporaryFile(delete=False, suffix=".txt") as f: + f.write(b"data") + path = f.name + try: + with pytest.raises(RuntimeError, match="Missing Location header"): + await client._upload_file_chunk("accounts", "guid-1", "new_doc", path) + finally: + os.unlink(path) + + async def test_invalid_chunk_size_falls_back_to_default(self): + """A non-integer x-ms-chunk-size header is ignored and the 4MB default is used.""" + client = _make_client() + location = "https://example.crm.dynamics.com/api/data/v9.2/accounts(guid-1)/new_doc?tok=x" + init_resp = _resp(status=200, headers={"Location": location, "x-ms-chunk-size": "invalid"}) + chunk_resp = _resp(status=204) + client._request.side_effect = [init_resp, chunk_resp] + with tempfile.NamedTemporaryFile(delete=False, suffix=".txt") as f: + f.write(b"hello") + path = f.name + try: + await client._upload_file_chunk("accounts", "guid-1", "new_doc", path) + finally: + os.unlink(path) + + +# --------------------------------------------------------------------------- +# _upload_file() — auto mode dispatch +# --------------------------------------------------------------------------- + + +class TestUploadFile: + """Tests for _upload_file(), the high-level dispatcher that selects the upload path.""" + + async def test_small_file_uses_small_mode(self): + """mode='small' routes to _upload_file_small without calling _upload_file_chunk.""" + client = _make_client() + _seed_cache(client) + client._get_entity_by_table_schema_name = AsyncMock( + return_value={"MetadataId": "meta-1", "LogicalName": "account"} + ) + client._get_attribute_metadata = AsyncMock(return_value={"MetadataId": "attr-1"}) + client._upload_file_small = AsyncMock(return_value=None) + client._upload_file_chunk = AsyncMock(return_value=None) + with tempfile.NamedTemporaryFile(delete=False, suffix=".txt") as f: + f.write(b"small content") + path = f.name + try: + await client._upload_file("account", "guid-1", "new_doc", path, mode="small") + client._upload_file_small.assert_called_once() + client._upload_file_chunk.assert_not_called() + finally: + os.unlink(path) + + async def test_chunk_mode_uses_chunk_upload(self): + """mode='chunk' routes to _upload_file_chunk without calling _upload_file_small.""" + client = _make_client() + _seed_cache(client) + client._get_entity_by_table_schema_name = AsyncMock( + return_value={"MetadataId": "meta-1", "LogicalName": "account"} + ) + client._get_attribute_metadata = AsyncMock(return_value={"MetadataId": "attr-1"}) + client._upload_file_small = AsyncMock(return_value=None) + client._upload_file_chunk = AsyncMock(return_value=None) + with tempfile.NamedTemporaryFile(delete=False, suffix=".bin") as f: + f.write(b"big content") + path = f.name + try: + await client._upload_file("account", "guid-1", "new_doc", path, mode="chunk") + client._upload_file_chunk.assert_called_once() + finally: + os.unlink(path) + + async def test_invalid_mode_raises(self): + """ValueError is raised when an unrecognised mode string is supplied.""" + client = _make_client() + _seed_cache(client) + client._get_entity_by_table_schema_name = AsyncMock( + return_value={"MetadataId": "meta-1", "LogicalName": "account"} + ) + client._get_attribute_metadata = AsyncMock(return_value={"MetadataId": "attr-1"}) + with tempfile.NamedTemporaryFile(delete=False, suffix=".txt") as f: + f.write(b"data") + path = f.name + try: + with pytest.raises(ValueError, match="Invalid mode"): + await client._upload_file("account", "guid-1", "new_doc", path, mode="badmode") + finally: + os.unlink(path) + + async def test_auto_mode_file_not_found_raises(self): + """FileNotFoundError is raised in default auto mode when the file path does not exist.""" + client = _make_client() + _seed_cache(client) + client._get_entity_by_table_schema_name = AsyncMock( + return_value={"MetadataId": "meta-1", "LogicalName": "account"} + ) + client._get_attribute_metadata = AsyncMock(return_value={"MetadataId": "attr-1"}) + with pytest.raises(FileNotFoundError): + await client._upload_file("account", "guid-1", "new_doc", "/nonexistent/file.txt") + + async def test_attribute_not_found_creates_it(self): + """When attribute metadata is missing, _create_columns and _wait_for_attribute_visibility are called.""" + client = _make_client() + _seed_cache(client) + client._get_entity_by_table_schema_name = AsyncMock( + return_value={"MetadataId": "meta-1", "LogicalName": "account"} + ) + client._get_attribute_metadata = AsyncMock(return_value=None) + client._create_columns = AsyncMock(return_value=["new_doc"]) + client._wait_for_attribute_visibility = AsyncMock(return_value=None) + client._upload_file_small = AsyncMock(return_value=None) + with tempfile.NamedTemporaryFile(delete=False, suffix=".txt") as f: + f.write(b"data") + path = f.name + try: + await client._upload_file("account", "guid-1", "new_doc", path, mode="small") + client._create_columns.assert_called_once_with("account", {"new_doc": "file"}) + client._wait_for_attribute_visibility.assert_called_once() + finally: + os.unlink(path) diff --git a/tests/unit/aio/test_async_batch.py b/tests/unit/aio/test_async_batch.py new file mode 100644 index 00000000..baad3ee6 --- /dev/null +++ b/tests/unit/aio/test_async_batch.py @@ -0,0 +1,511 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +import pytest +import pandas as pd +from unittest.mock import AsyncMock + + +from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient +from PowerPlatform.Dataverse.aio.operations.async_batch import ( + AsyncBatchOperations, + AsyncBatchRequest, + AsyncChangeSet, +) +from PowerPlatform.Dataverse.operations.batch import ( + BatchRecordOperations, + BatchTableOperations, + BatchQueryOperations, + BatchDataFrameOperations, + ChangeSetRecordOperations, +) +from PowerPlatform.Dataverse.data._batch_base import ( + _RecordCreate, + _RecordUpdate, + _RecordDelete, + _RecordGet, + _RecordUpsert, + _TableCreate, + _TableDelete, + _TableGet, + _TableList, + _TableAddColumns, + _TableRemoveColumns, + _TableCreateOneToMany, + _TableCreateManyToMany, + _TableDeleteRelationship, + _TableGetRelationship, + _TableCreateLookupField, + _QuerySql, + _ChangeSet, +) +from PowerPlatform.Dataverse.models.upsert import UpsertItem +from PowerPlatform.Dataverse.models.relationship import ( + LookupAttributeMetadata, + OneToManyRelationshipMetadata, + ManyToManyRelationshipMetadata, +) +from PowerPlatform.Dataverse.models.labels import Label, LocalizedLabel + + +def _label(text: str = "Test") -> Label: + return Label(localized_labels=[LocalizedLabel(label=text, language_code=1033)]) + + +from PowerPlatform.Dataverse.core.errors import ValidationError + +# --------------------------------------------------------------------------- +# Fixtures +# --------------------------------------------------------------------------- + + +def _make_batch(async_client: AsyncDataverseClient) -> AsyncBatchRequest: + return async_client.batch.new() + + +# --------------------------------------------------------------------------- +# Namespace tests +# --------------------------------------------------------------------------- + + +class TestAsyncBatchOperationsNamespace: + def test_namespace_type(self, async_client): + assert isinstance(async_client.batch, AsyncBatchOperations) + + def test_new_returns_batch_request(self, async_client): + batch = async_client.batch.new() + assert isinstance(batch, AsyncBatchRequest) + + def test_batch_request_namespaces(self, async_client): + batch = async_client.batch.new() + assert isinstance(batch.records, BatchRecordOperations) + assert isinstance(batch.tables, BatchTableOperations) + assert isinstance(batch.query, BatchQueryOperations) + assert isinstance(batch.dataframe, BatchDataFrameOperations) + + +# --------------------------------------------------------------------------- +# AsyncBatchRecordOperations +# --------------------------------------------------------------------------- + + +class TestAsyncBatchRecordOperations: + def test_create_single_appends_record_create(self, async_client): + batch = _make_batch(async_client) + batch.records.create("account", {"name": "Contoso"}) + assert len(batch._items) == 1 + item = batch._items[0] + assert isinstance(item, _RecordCreate) + assert item.table == "account" + assert item.data == {"name": "Contoso"} + + def test_create_bulk_appends_record_create(self, async_client): + batch = _make_batch(async_client) + batch.records.create("account", [{"name": "A"}, {"name": "B"}]) + assert len(batch._items) == 1 + assert isinstance(batch._items[0], _RecordCreate) + + def test_update_single_appends_record_update(self, async_client): + batch = _make_batch(async_client) + batch.records.update("account", "guid-1", {"name": "X"}) + assert len(batch._items) == 1 + item = batch._items[0] + assert isinstance(item, _RecordUpdate) + assert item.table == "account" + + def test_delete_single_appends_record_delete(self, async_client): + batch = _make_batch(async_client) + batch.records.delete("account", "guid-1") + assert len(batch._items) == 1 + item = batch._items[0] + assert isinstance(item, _RecordDelete) + + def test_delete_bulk_appends_record_delete(self, async_client): + batch = _make_batch(async_client) + batch.records.delete("account", ["guid-1", "guid-2"]) + assert isinstance(batch._items[0], _RecordDelete) + + def test_get_appends_record_get(self, async_client): + batch = _make_batch(async_client) + with pytest.warns(DeprecationWarning): + batch.records.get("account", "guid-1", select=["name"]) + assert len(batch._items) == 1 + item = batch._items[0] + assert isinstance(item, _RecordGet) + assert item.table == "account" + assert item.record_id == "guid-1" + assert item.select == ["name"] + + def test_upsert_appends_record_upsert(self, async_client): + batch = _make_batch(async_client) + item = UpsertItem(alternate_key={"accountnumber": "ACC-001"}, record={"name": "X"}) + batch.records.upsert("account", [item]) + assert len(batch._items) == 1 + assert isinstance(batch._items[0], _RecordUpsert) + + def test_upsert_dict_item_normalized(self, async_client): + batch = _make_batch(async_client) + batch.records.upsert("account", [{"alternate_key": {"accountnumber": "ACC-001"}, "record": {"name": "X"}}]) + enqueued = batch._items[0] + assert isinstance(enqueued, _RecordUpsert) + assert isinstance(enqueued.items[0], UpsertItem) + + def test_upsert_empty_list_raises(self, async_client): + batch = _make_batch(async_client) + with pytest.raises(TypeError): + batch.records.upsert("account", []) + + def test_upsert_invalid_item_raises(self, async_client): + batch = _make_batch(async_client) + with pytest.raises(TypeError): + batch.records.upsert("account", [42]) + + +# --------------------------------------------------------------------------- +# AsyncBatchTableOperations +# --------------------------------------------------------------------------- + + +class TestAsyncBatchTableOperations: + def test_create_appends_table_create(self, async_client): + batch = _make_batch(async_client) + batch.tables.create("new_Product", {"new_Price": "decimal"}) + assert len(batch._items) == 1 + item = batch._items[0] + assert isinstance(item, _TableCreate) + assert item.table == "new_Product" + + def test_delete_appends_table_delete(self, async_client): + batch = _make_batch(async_client) + batch.tables.delete("new_Product") + assert isinstance(batch._items[0], _TableDelete) + + def test_get_appends_table_get(self, async_client): + batch = _make_batch(async_client) + batch.tables.get("new_Product") + assert isinstance(batch._items[0], _TableGet) + + def test_list_appends_table_list(self, async_client): + batch = _make_batch(async_client) + batch.tables.list() + assert isinstance(batch._items[0], _TableList) + + def test_add_columns_appends(self, async_client): + batch = _make_batch(async_client) + batch.tables.add_columns("new_Product", {"new_Notes": "string"}) + assert isinstance(batch._items[0], _TableAddColumns) + + def test_remove_columns_appends(self, async_client): + batch = _make_batch(async_client) + batch.tables.remove_columns("new_Product", "new_Notes") + assert isinstance(batch._items[0], _TableRemoveColumns) + + def test_create_one_to_many_appends(self, async_client): + batch = _make_batch(async_client) + lookup = LookupAttributeMetadata(schema_name="new_DeptId", display_name=_label("Department")) + rel = OneToManyRelationshipMetadata( + schema_name="new_Dept_Emp", + referenced_entity="new_dept", + referencing_entity="new_emp", + referenced_attribute="new_deptid", + ) + batch.tables.create_one_to_many_relationship(lookup, rel) + assert isinstance(batch._items[0], _TableCreateOneToMany) + + def test_create_many_to_many_appends(self, async_client): + batch = _make_batch(async_client) + rel = ManyToManyRelationshipMetadata( + schema_name="new_emp_proj", + entity1_logical_name="new_emp", + entity2_logical_name="new_proj", + ) + batch.tables.create_many_to_many_relationship(rel) + assert isinstance(batch._items[0], _TableCreateManyToMany) + + def test_delete_relationship_appends(self, async_client): + batch = _make_batch(async_client) + batch.tables.delete_relationship("rel-guid") + assert isinstance(batch._items[0], _TableDeleteRelationship) + + def test_get_relationship_appends(self, async_client): + batch = _make_batch(async_client) + batch.tables.get_relationship("new_Dept_Emp") + assert isinstance(batch._items[0], _TableGetRelationship) + + def test_create_lookup_field_appends(self, async_client): + batch = _make_batch(async_client) + batch.tables.create_lookup_field( + referencing_table="new_order", + lookup_field_name="new_AccountId", + referenced_table="account", + ) + assert isinstance(batch._items[0], _TableCreateLookupField) + + +# --------------------------------------------------------------------------- +# AsyncBatchQueryOperations +# --------------------------------------------------------------------------- + + +class TestAsyncBatchQueryOperations: + def test_sql_appends_query_sql(self, async_client): + batch = _make_batch(async_client) + batch.query.sql("SELECT name FROM account") + assert len(batch._items) == 1 + item = batch._items[0] + assert isinstance(item, _QuerySql) + assert item.sql == "SELECT name FROM account" + + def test_sql_strips_whitespace(self, async_client): + batch = _make_batch(async_client) + batch.query.sql(" SELECT name FROM account ") + assert batch._items[0].sql == "SELECT name FROM account" + + def test_sql_empty_string_raises(self, async_client): + batch = _make_batch(async_client) + with pytest.raises(ValidationError): + batch.query.sql("") + + def test_sql_whitespace_only_raises(self, async_client): + batch = _make_batch(async_client) + with pytest.raises(ValidationError): + batch.query.sql(" ") + + def test_sql_non_string_raises(self, async_client): + batch = _make_batch(async_client) + with pytest.raises(ValidationError): + batch.query.sql(None) + + +# --------------------------------------------------------------------------- +# AsyncBatchDataFrameOperations +# --------------------------------------------------------------------------- + + +class TestAsyncBatchDataFrameOperations: + def test_create_from_dataframe(self, async_client): + batch = _make_batch(async_client) + df = pd.DataFrame([{"name": "Contoso"}, {"name": "Fabrikam"}]) + batch.dataframe.create("account", df) + assert len(batch._items) == 1 + assert isinstance(batch._items[0], _RecordCreate) + + def test_create_non_dataframe_raises(self, async_client): + batch = _make_batch(async_client) + with pytest.raises(TypeError): + batch.dataframe.create("account", [{"name": "X"}]) + + def test_create_empty_dataframe_raises(self, async_client): + batch = _make_batch(async_client) + with pytest.raises(ValueError): + batch.dataframe.create("account", pd.DataFrame()) + + def test_create_all_null_row_raises(self, async_client): + batch = _make_batch(async_client) + with pytest.raises(ValueError): + batch.dataframe.create("account", pd.DataFrame([{"name": None}])) + + def test_update_from_dataframe(self, async_client): + batch = _make_batch(async_client) + df = pd.DataFrame([{"accountid": "guid-1", "name": "X"}]) + batch.dataframe.update("account", df, id_column="accountid") + assert len(batch._items) == 1 + assert isinstance(batch._items[0], _RecordUpdate) + + def test_update_non_dataframe_raises(self, async_client): + batch = _make_batch(async_client) + with pytest.raises(TypeError): + batch.dataframe.update("account", [{}], id_column="id") + + def test_update_empty_dataframe_raises(self, async_client): + batch = _make_batch(async_client) + with pytest.raises(ValueError): + batch.dataframe.update("account", pd.DataFrame(), id_column="id") + + def test_update_missing_id_column_raises(self, async_client): + batch = _make_batch(async_client) + df = pd.DataFrame([{"name": "X"}]) + with pytest.raises(ValueError, match="id_column"): + batch.dataframe.update("account", df, id_column="accountid") + + def test_update_invalid_ids_raises(self, async_client): + batch = _make_batch(async_client) + df = pd.DataFrame([{"accountid": None, "name": "X"}]) + with pytest.raises(ValueError): + batch.dataframe.update("account", df, id_column="accountid") + + def test_update_no_change_columns_raises(self, async_client): + batch = _make_batch(async_client) + df = pd.DataFrame([{"accountid": "guid-1"}]) + with pytest.raises(ValueError): + batch.dataframe.update("account", df, id_column="accountid") + + def test_update_all_null_rows_skipped(self, async_client): + batch = _make_batch(async_client) + df = pd.DataFrame([{"accountid": "guid-1", "telephone1": None}]) + batch.dataframe.update("account", df, id_column="accountid") + # All change values null -> nothing enqueued + assert len(batch._items) == 0 + + def test_delete_from_series(self, async_client): + batch = _make_batch(async_client) + ids = pd.Series(["guid-1", "guid-2"]) + batch.dataframe.delete("account", ids) + assert len(batch._items) == 1 + assert isinstance(batch._items[0], _RecordDelete) + + def test_delete_non_series_raises(self, async_client): + batch = _make_batch(async_client) + with pytest.raises(TypeError): + batch.dataframe.delete("account", ["guid-1"]) + + def test_delete_empty_series_no_item(self, async_client): + batch = _make_batch(async_client) + batch.dataframe.delete("account", pd.Series([], dtype=str)) + assert len(batch._items) == 0 + + def test_delete_invalid_ids_raises(self, async_client): + batch = _make_batch(async_client) + ids = pd.Series(["guid-1", None]) + with pytest.raises(ValueError): + batch.dataframe.delete("account", ids) + + +# --------------------------------------------------------------------------- +# AsyncChangeSet +# --------------------------------------------------------------------------- + + +class TestAsyncChangeSet: + def test_changeset_returns_async_changeset(self, async_client): + batch = _make_batch(async_client) + cs = batch.changeset() + assert isinstance(cs, AsyncChangeSet) + + def test_changeset_records_namespace(self, async_client): + batch = _make_batch(async_client) + cs = batch.changeset() + assert isinstance(cs.records, ChangeSetRecordOperations) + + def test_changeset_appended_to_items(self, async_client): + batch = _make_batch(async_client) + batch.changeset() + assert len(batch._items) == 1 + assert isinstance(batch._items[0], _ChangeSet) + + async def test_changeset_async_context_manager(self, async_client): + batch = _make_batch(async_client) + async with batch.changeset() as cs: + assert isinstance(cs, AsyncChangeSet) + + +class TestAsyncChangeSetRecordOperations: + def test_create_adds_to_changeset(self, async_client): + batch = _make_batch(async_client) + cs = batch.changeset() + ref = cs.records.create("account", {"name": "Contoso"}) + # ref should be a content-ID string like "$1" + assert isinstance(ref, str) + assert ref.startswith("$") + + def test_update_adds_to_changeset(self, async_client): + batch = _make_batch(async_client) + cs = batch.changeset() + cs.records.update("account", "guid-1", {"name": "X"}) + internal = batch._items[0] + assert len(internal.operations) == 1 + + def test_delete_adds_to_changeset(self, async_client): + batch = _make_batch(async_client) + cs = batch.changeset() + cs.records.delete("account", "guid-1") + internal = batch._items[0] + assert len(internal.operations) == 1 + + def test_content_id_increments(self, async_client): + batch = _make_batch(async_client) + cs = batch.changeset() + ref1 = cs.records.create("account", {"name": "A"}) + ref2 = cs.records.create("contact", {"firstname": "B"}) + assert ref1 != ref2 + + +# --------------------------------------------------------------------------- +# AsyncBatchRequest.execute +# --------------------------------------------------------------------------- + + +class TestAsyncBatchRequestExecute: + async def test_execute_calls_batch_client(self, async_client, mock_od): + """execute() delegates to _AsyncBatchClient and returns BatchResult.""" + from PowerPlatform.Dataverse.models.batch import BatchResult, BatchItemResponse + + mock_result = BatchResult(responses=[BatchItemResponse(status_code=204)]) + + # Patch _AsyncBatchClient so we don't need a real HTTP client + with __import__("unittest.mock", fromlist=["patch"]).patch( + "PowerPlatform.Dataverse.aio.operations.async_batch._AsyncBatchClient" + ) as mock_cls: + mock_instance = AsyncMock() + mock_instance.execute.return_value = mock_result + mock_cls.return_value = mock_instance + + batch = _make_batch(async_client) + batch.records.create("account", {"name": "X"}) + result = await batch.execute() + + mock_instance.execute.assert_called_once() + assert isinstance(result, BatchResult) + + async def test_execute_passes_continue_on_error(self, async_client, mock_od): + """execute() passes continue_on_error to _AsyncBatchClient.execute.""" + from PowerPlatform.Dataverse.models.batch import BatchResult + + mock_result = BatchResult() + + with __import__("unittest.mock", fromlist=["patch"]).patch( + "PowerPlatform.Dataverse.aio.operations.async_batch._AsyncBatchClient" + ) as mock_cls: + mock_instance = AsyncMock() + mock_instance.execute.return_value = mock_result + mock_cls.return_value = mock_instance + + batch = _make_batch(async_client) + await batch.execute(continue_on_error=True) + + _, kwargs = mock_instance.execute.call_args + assert kwargs["continue_on_error"] is True + + async def test_execute_empty_batch_ok(self, async_client, mock_od): + """execute() with an empty batch does not raise.""" + from PowerPlatform.Dataverse.models.batch import BatchResult + + mock_result = BatchResult() + + with __import__("unittest.mock", fromlist=["patch"]).patch( + "PowerPlatform.Dataverse.aio.operations.async_batch._AsyncBatchClient" + ) as mock_cls: + mock_instance = AsyncMock() + mock_instance.execute.return_value = mock_result + mock_cls.return_value = mock_instance + + batch = _make_batch(async_client) + result = await batch.execute() + + assert isinstance(result, BatchResult) + + +# --------------------------------------------------------------------------- +# Multiple operations in one batch +# --------------------------------------------------------------------------- + + +class TestAsyncBatchMultipleOperations: + def test_multiple_items_accumulated(self, async_client): + batch = _make_batch(async_client) + batch.records.create("account", {"name": "A"}) + with pytest.warns(DeprecationWarning): + batch.records.get("account", "guid-1") + batch.tables.get("account") + batch.query.sql("SELECT name FROM account") + assert len(batch._items) == 4 diff --git a/tests/unit/aio/test_async_client.py b/tests/unit/aio/test_async_client.py new file mode 100644 index 00000000..5997a30a --- /dev/null +++ b/tests/unit/aio/test_async_client.py @@ -0,0 +1,241 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +import pytest +from unittest.mock import AsyncMock, MagicMock, patch + +from azure.core.credentials_async import AsyncTokenCredential + +from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient +from PowerPlatform.Dataverse.aio.operations.async_records import AsyncRecordOperations +from PowerPlatform.Dataverse.aio.operations.async_tables import AsyncTableOperations +from PowerPlatform.Dataverse.aio.operations.async_query import AsyncQueryOperations +from PowerPlatform.Dataverse.aio.operations.async_files import AsyncFileOperations +from PowerPlatform.Dataverse.aio.operations.async_dataframe import AsyncDataFrameOperations +from PowerPlatform.Dataverse.aio.operations.async_batch import AsyncBatchOperations +from PowerPlatform.Dataverse.core.config import DataverseConfig, OperationContext + + +def _make_credential() -> MagicMock: + return MagicMock(spec=AsyncTokenCredential) + + +class TestAsyncDataverseClientInit: + """Tests for AsyncDataverseClient initialization and validation.""" + + def test_valid_init(self): + """AsyncDataverseClient initializes with valid url and credential.""" + client = AsyncDataverseClient("https://org.crm.dynamics.com", _make_credential()) + assert client._base_url == "https://org.crm.dynamics.com" + assert not client._closed + + def test_trailing_slash_stripped(self): + """Trailing slash is stripped from base_url.""" + client = AsyncDataverseClient("https://org.crm.dynamics.com/", _make_credential()) + assert client._base_url == "https://org.crm.dynamics.com" + + def test_empty_base_url_raises(self): + """Empty base_url raises ValueError.""" + with pytest.raises(ValueError, match="base_url is required"): + AsyncDataverseClient("", _make_credential()) + + def test_namespace_attributes_created(self): + """All operation namespace attributes are created.""" + client = AsyncDataverseClient("https://org.crm.dynamics.com", _make_credential()) + assert isinstance(client.records, AsyncRecordOperations) + assert isinstance(client.tables, AsyncTableOperations) + assert isinstance(client.query, AsyncQueryOperations) + assert isinstance(client.files, AsyncFileOperations) + assert isinstance(client.dataframe, AsyncDataFrameOperations) + assert isinstance(client.batch, AsyncBatchOperations) + + def test_odata_and_session_initially_none(self): + """_odata and _session are None until first use.""" + client = AsyncDataverseClient("https://org.crm.dynamics.com", _make_credential()) + assert client._odata is None + assert client._session is None + + +class TestAsyncDataverseClientContextManager: + """Tests for async context manager protocol.""" + + async def test_aenter_returns_self(self): + """__aenter__ returns the client instance.""" + client = AsyncDataverseClient("https://org.crm.dynamics.com", _make_credential()) + with patch("aiohttp.ClientSession") as mock_session_cls: + mock_session_cls.return_value = MagicMock() + result = await client.__aenter__() + assert result is client + + async def test_aenter_creates_session(self): + """__aenter__ creates an aiohttp.ClientSession.""" + client = AsyncDataverseClient("https://org.crm.dynamics.com", _make_credential()) + with patch("aiohttp.ClientSession") as mock_session_cls: + mock_session_cls.return_value = MagicMock() + await client.__aenter__() + mock_session_cls.assert_called_once() + assert client._session is not None + + async def test_aenter_does_not_recreate_existing_session(self): + """__aenter__ does not replace an existing session.""" + client = AsyncDataverseClient("https://org.crm.dynamics.com", _make_credential()) + existing_session = MagicMock() + client._session = existing_session + with patch("aiohttp.ClientSession") as mock_session_cls: + await client.__aenter__() + mock_session_cls.assert_not_called() + assert client._session is existing_session + + async def test_aexit_calls_aclose(self): + """__aexit__ calls aclose() to release resources.""" + client = AsyncDataverseClient("https://org.crm.dynamics.com", _make_credential()) + client.aclose = AsyncMock() + await client.__aexit__(None, None, None) + client.aclose.assert_called_once() + + async def test_aenter_raises_after_close(self): + """__aenter__ raises RuntimeError after the client has been closed.""" + client = AsyncDataverseClient("https://org.crm.dynamics.com", _make_credential()) + client._closed = True + with pytest.raises(RuntimeError, match="closed"): + await client.__aenter__() + + +class TestAsyncDataverseClientAclose: + """Tests for aclose() lifecycle.""" + + async def test_aclose_sets_closed_flag(self): + """aclose() marks the client as closed.""" + client = AsyncDataverseClient("https://org.crm.dynamics.com", _make_credential()) + await client.aclose() + assert client._closed + + async def test_aclose_closes_session(self): + """aclose() closes the aiohttp.ClientSession.""" + client = AsyncDataverseClient("https://org.crm.dynamics.com", _make_credential()) + mock_session = MagicMock() + mock_session.close = AsyncMock() + client._session = mock_session + await client.aclose() + mock_session.close.assert_called_once() + assert client._session is None + + async def test_aclose_closes_odata(self): + """aclose() closes the internal _odata client.""" + client = AsyncDataverseClient("https://org.crm.dynamics.com", _make_credential()) + mock_odata = AsyncMock() + client._odata = mock_odata + await client.aclose() + mock_odata.close.assert_called_once() + assert client._odata is None + + async def test_aclose_idempotent(self): + """aclose() is safe to call multiple times.""" + client = AsyncDataverseClient("https://org.crm.dynamics.com", _make_credential()) + await client.aclose() + await client.aclose() # should not raise + assert client._closed + + async def test_context_manager_closes_on_exit(self): + """Using async with calls aclose() on exit.""" + client = AsyncDataverseClient("https://org.crm.dynamics.com", _make_credential()) + with patch("aiohttp.ClientSession") as mock_session_cls: + mock_session = MagicMock() + mock_session.close = AsyncMock() + mock_session_cls.return_value = mock_session + async with client: + pass + assert client._closed + + +class TestAsyncDataverseClientCheckClosed: + """Tests for _check_closed guard.""" + + def test_check_closed_raises_when_closed(self): + """_check_closed() raises RuntimeError when the client is closed.""" + client = AsyncDataverseClient("https://org.crm.dynamics.com", _make_credential()) + client._closed = True + with pytest.raises(RuntimeError, match="closed"): + client._check_closed() + + def test_check_closed_does_not_raise_when_open(self): + """_check_closed() does not raise when the client is open.""" + client = AsyncDataverseClient("https://org.crm.dynamics.com", _make_credential()) + client._check_closed() # should not raise + + +class TestAsyncDataverseClientGetOdata: + """Tests for _get_odata() lazy initialisation of the internal OData client.""" + + async def test_get_odata_creates_client_on_first_call(self): + """_get_odata() instantiates _AsyncODataClient and stores it in _odata on first call.""" + from PowerPlatform.Dataverse.aio.data._async_odata import _AsyncODataClient + + client = AsyncDataverseClient("https://org.crm.dynamics.com", _make_credential()) + assert client._odata is None + od = client._get_odata() + assert isinstance(od, _AsyncODataClient) + assert client._odata is od + await client.aclose() + + async def test_get_odata_returns_same_instance(self): + """Subsequent calls to _get_odata() return the same cached instance.""" + client = AsyncDataverseClient("https://org.crm.dynamics.com", _make_credential()) + od1 = client._get_odata() + od2 = client._get_odata() + assert od1 is od2 + await client.aclose() + + +class TestAsyncDataverseClientScopedOdata: + """Tests for _scoped_odata(), an async context manager that guards OData client access.""" + + async def test_scoped_odata_yields_odata_client(self): + """_scoped_odata() yields the low-level _AsyncODataClient instance.""" + from PowerPlatform.Dataverse.aio.data._async_odata import _AsyncODataClient + + client = AsyncDataverseClient("https://org.crm.dynamics.com", _make_credential()) + async with client._scoped_odata() as od: + assert isinstance(od, _AsyncODataClient) + + async def test_scoped_odata_raises_when_closed(self): + """RuntimeError is raised when _scoped_odata() is entered after the client is closed.""" + client = AsyncDataverseClient("https://org.crm.dynamics.com", _make_credential()) + client._closed = True + with pytest.raises(RuntimeError, match="closed"): + async with client._scoped_odata(): + pass + + +class TestAsyncDataverseClientOperationContext: + """Tests for the context= kwarg on AsyncDataverseClient.""" + + def test_context_kwarg_sets_config(self): + """context= stores OperationContext in _config.operation_context.""" + ctx = OperationContext(user_agent_context="app=test/1.0;agent=claude-code") + client = AsyncDataverseClient("https://org.crm.dynamics.com", _make_credential(), context=ctx) + assert client._config.operation_context.user_agent_context == "app=test/1.0;agent=claude-code" + + def test_no_context_leaves_config_default(self): + """Without context=, operation_context defaults to None.""" + client = AsyncDataverseClient("https://org.crm.dynamics.com", _make_credential()) + assert client._config.operation_context is None + + def test_config_and_context_raises(self): + """Providing both config= and context= raises ValueError.""" + ctx = OperationContext(user_agent_context="app=test/1.0") + config = DataverseConfig(operation_context=ctx) + with pytest.raises(ValueError, match="config.*context|context.*config"): + AsyncDataverseClient( + "https://org.crm.dynamics.com", + _make_credential(), + config=config, + context=OperationContext(user_agent_context="app=other/2.0"), + ) + + def test_config_alone_works(self): + """Providing config= without context= uses config's operation_context.""" + ctx = OperationContext(user_agent_context="app=test/1.0;skill=dv") + config = DataverseConfig(operation_context=ctx) + client = AsyncDataverseClient("https://org.crm.dynamics.com", _make_credential(), config=config) + assert client._config.operation_context.user_agent_context == "app=test/1.0;skill=dv" diff --git a/tests/unit/aio/test_async_dataframe.py b/tests/unit/aio/test_async_dataframe.py new file mode 100644 index 00000000..36b69760 --- /dev/null +++ b/tests/unit/aio/test_async_dataframe.py @@ -0,0 +1,183 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +import pytest +import pandas as pd +from contextlib import asynccontextmanager +from unittest.mock import MagicMock + +from azure.core.credentials_async import AsyncTokenCredential + +from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient +from PowerPlatform.Dataverse.aio.operations.async_dataframe import AsyncDataFrameOperations + + +def _make_client_with_od(mock_od): + cred = MagicMock(spec=AsyncTokenCredential) + client = AsyncDataverseClient("https://example.crm.dynamics.com", cred) + + @asynccontextmanager + async def _fake_scoped(): + yield mock_od + + client._scoped_odata = _fake_scoped + return client + + +class TestAsyncDataFrameOperationsNamespace: + def test_namespace_type(self, async_client): + assert isinstance(async_client.dataframe, AsyncDataFrameOperations) + + +class TestAsyncDataFrameSql: + async def test_sql_returns_dataframe(self, async_client, mock_od): + """sql() executes a SQL query and returns a DataFrame.""" + mock_od._query_sql.return_value = [ + {"name": "Contoso", "accountid": "guid-1"}, + {"name": "Fabrikam", "accountid": "guid-2"}, + ] + + df = await async_client.dataframe.sql("SELECT name FROM account") + + assert isinstance(df, pd.DataFrame) + assert len(df) == 2 + assert "name" in df.columns + + async def test_sql_empty_result_returns_empty_dataframe(self, async_client, mock_od): + """sql() returns an empty DataFrame when no rows match.""" + mock_od._query_sql.return_value = [] + df = await async_client.dataframe.sql("SELECT name FROM account WHERE 1=0") + assert isinstance(df, pd.DataFrame) + assert len(df) == 0 + + +class TestAsyncDataFrameCreate: + async def test_create_returns_series_of_guids(self, async_client, mock_od): + """create() returns a Series of GUIDs aligned with the input DataFrame.""" + mock_od._entity_set_from_schema_name.return_value = "accounts" + mock_od._create_multiple.return_value = ["guid-1", "guid-2"] + + df = pd.DataFrame([{"name": "Contoso"}, {"name": "Fabrikam"}]) + result = await async_client.dataframe.create("account", df) + + assert isinstance(result, pd.Series) + assert list(result) == ["guid-1", "guid-2"] + + async def test_create_non_dataframe_raises(self, async_client, mock_od): + """create() raises TypeError if records is not a DataFrame.""" + with pytest.raises(TypeError): + await async_client.dataframe.create("account", [{"name": "X"}]) + + async def test_create_empty_dataframe_raises(self, async_client, mock_od): + """create() raises ValueError if records is empty.""" + with pytest.raises(ValueError): + await async_client.dataframe.create("account", pd.DataFrame()) + + async def test_create_all_null_row_raises(self, async_client, mock_od): + """create() raises ValueError if any row has no non-null values.""" + mock_od._entity_set_from_schema_name.return_value = "accounts" + df = pd.DataFrame([{"name": None}]) + with pytest.raises(ValueError, match="no non-null values"): + await async_client.dataframe.create("account", df) + + async def test_create_id_count_mismatch_raises(self, async_client, mock_od): + """create() raises ValueError if the server returns wrong number of IDs.""" + mock_od._entity_set_from_schema_name.return_value = "accounts" + mock_od._create_multiple.return_value = ["guid-1"] # 1 ID for 2 rows + + df = pd.DataFrame([{"name": "A"}, {"name": "B"}]) + with pytest.raises(ValueError, match="returned"): + await async_client.dataframe.create("account", df) + + +class TestAsyncDataFrameUpdate: + async def test_update_single_row(self, async_client, mock_od): + """update() with a single-row DataFrame calls records.update once.""" + df = pd.DataFrame([{"accountid": "guid-1", "telephone1": "555"}]) + await async_client.dataframe.update("account", df, id_column="accountid") + mock_od._update.assert_called_once_with("account", "guid-1", {"telephone1": "555"}) + + async def test_update_multiple_rows(self, async_client, mock_od): + """update() with multiple rows calls records.update with lists.""" + df = pd.DataFrame( + [ + {"accountid": "guid-1", "telephone1": "555"}, + {"accountid": "guid-2", "telephone1": "666"}, + ] + ) + await async_client.dataframe.update("account", df, id_column="accountid") + mock_od._update_by_ids.assert_called_once_with( + "account", + ["guid-1", "guid-2"], + [{"telephone1": "555"}, {"telephone1": "666"}], + ) + + async def test_update_non_dataframe_raises(self, async_client, mock_od): + """update() raises TypeError if changes is not a DataFrame.""" + with pytest.raises(TypeError): + await async_client.dataframe.update("account", [{}], id_column="id") + + async def test_update_empty_dataframe_raises(self, async_client, mock_od): + """update() raises ValueError if changes is empty.""" + with pytest.raises(ValueError): + await async_client.dataframe.update("account", pd.DataFrame(), id_column="id") + + async def test_update_missing_id_column_raises(self, async_client, mock_od): + """update() raises ValueError if id_column is not in the DataFrame.""" + df = pd.DataFrame([{"name": "X"}]) + with pytest.raises(ValueError, match="id_column"): + await async_client.dataframe.update("account", df, id_column="accountid") + + async def test_update_invalid_ids_raises(self, async_client, mock_od): + """update() raises ValueError if id_column contains invalid (non-string) values.""" + df = pd.DataFrame([{"accountid": None, "name": "X"}]) + with pytest.raises(ValueError, match="invalid values"): + await async_client.dataframe.update("account", df, id_column="accountid") + + async def test_update_no_change_columns_raises(self, async_client, mock_od): + """update() raises ValueError if no columns exist besides id_column.""" + df = pd.DataFrame([{"accountid": "guid-1"}]) + with pytest.raises(ValueError, match="No columns to update"): + await async_client.dataframe.update("account", df, id_column="accountid") + + async def test_update_all_null_rows_skipped(self, async_client, mock_od): + """update() skips rows where all change values are NaN/None.""" + df = pd.DataFrame([{"accountid": "guid-1", "telephone1": None}]) + await async_client.dataframe.update("account", df, id_column="accountid") + # All values are null -> no updates sent + mock_od._update.assert_not_called() + mock_od._update_by_ids.assert_not_called() + + +class TestAsyncDataFrameDelete: + async def test_delete_single(self, async_client, mock_od): + """delete() with a single-element Series calls records.delete once.""" + ids = pd.Series(["guid-1"]) + result = await async_client.dataframe.delete("account", ids) + mock_od._delete.assert_called_once_with("account", "guid-1") + assert result is None + + async def test_delete_multiple_bulk(self, async_client, mock_od): + """delete() with multiple IDs and use_bulk_delete=True uses BulkDelete.""" + mock_od._delete_multiple.return_value = "job-guid" + ids = pd.Series(["guid-1", "guid-2"]) + result = await async_client.dataframe.delete("account", ids) + mock_od._delete_multiple.assert_called_once_with("account", ["guid-1", "guid-2"]) + assert result == "job-guid" + + async def test_delete_empty_series_returns_none(self, async_client, mock_od): + """delete() with an empty Series returns None without calling _delete.""" + result = await async_client.dataframe.delete("account", pd.Series([], dtype=str)) + mock_od._delete.assert_not_called() + assert result is None + + async def test_delete_non_series_raises(self, async_client, mock_od): + """delete() raises TypeError if ids is not a pandas Series.""" + with pytest.raises(TypeError): + await async_client.dataframe.delete("account", ["guid-1", "guid-2"]) + + async def test_delete_invalid_ids_raises(self, async_client, mock_od): + """delete() raises ValueError if any ID is not a non-empty string.""" + ids = pd.Series(["guid-1", None]) + with pytest.raises(ValueError, match="invalid values"): + await async_client.dataframe.delete("account", ids) diff --git a/tests/unit/aio/test_async_files.py b/tests/unit/aio/test_async_files.py new file mode 100644 index 00000000..9c326d07 --- /dev/null +++ b/tests/unit/aio/test_async_files.py @@ -0,0 +1,53 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + + +from PowerPlatform.Dataverse.aio.operations.async_files import AsyncFileOperations + + +class TestAsyncFileOperationsNamespace: + def test_namespace_type(self, async_client): + assert isinstance(async_client.files, AsyncFileOperations) + + +class TestAsyncFileUpload: + async def test_upload_delegates_to_upload_file(self, async_client, mock_od): + """upload() calls od._upload_file with all provided arguments.""" + await async_client.files.upload( + "account", + "guid-1", + "new_Document", + "/path/to/file.pdf", + mode="small", + mime_type="application/pdf", + if_none_match=False, + ) + + mock_od._upload_file.assert_called_once_with( + "account", + "guid-1", + "new_Document", + "/path/to/file.pdf", + mode="small", + mime_type="application/pdf", + if_none_match=False, + ) + + async def test_upload_default_args(self, async_client, mock_od): + """upload() passes None/True for optional args when not specified.""" + await async_client.files.upload("account", "guid-1", "new_Doc", "/path/file.txt") + + mock_od._upload_file.assert_called_once_with( + "account", + "guid-1", + "new_Doc", + "/path/file.txt", + mode=None, + mime_type=None, + if_none_match=True, + ) + + async def test_upload_returns_none(self, async_client, mock_od): + """upload() returns None.""" + result = await async_client.files.upload("account", "guid-1", "new_Doc", "/path/file.txt") + assert result is None diff --git a/tests/unit/aio/test_async_query.py b/tests/unit/aio/test_async_query.py new file mode 100644 index 00000000..3d867bd3 --- /dev/null +++ b/tests/unit/aio/test_async_query.py @@ -0,0 +1,542 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +import pytest +from contextlib import asynccontextmanager +from unittest.mock import AsyncMock, MagicMock + +from azure.core.credentials_async import AsyncTokenCredential + +from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient +from PowerPlatform.Dataverse.aio.operations.async_query import AsyncQueryOperations +from PowerPlatform.Dataverse.aio.models.async_fetchxml_query import AsyncFetchXmlQuery +from PowerPlatform.Dataverse.aio.models.async_query_builder import AsyncQueryBuilder +from PowerPlatform.Dataverse.models.record import QueryResult, Record + + +def _make_async_client_with_od(mock_od): + """Helper: create async client with mocked _scoped_odata.""" + cred = MagicMock(spec=AsyncTokenCredential) + client = AsyncDataverseClient("https://example.crm.dynamics.com", cred) + + @asynccontextmanager + async def _fake_scoped(): + yield mock_od + + client._scoped_odata = _fake_scoped + return client + + +_SIMPLE_FETCHXML = '' + + +class TestAsyncQueryOperationsNamespace: + def test_namespace_type(self, async_client): + assert isinstance(async_client.query, AsyncQueryOperations) + + def test_builder_returns_async_query_builder(self, async_client): + """builder() returns an AsyncQueryBuilder bound to this client.""" + qb = async_client.query.builder("account") + assert isinstance(qb, AsyncQueryBuilder) + assert qb._query_ops is async_client.query + + def test_fetchxml_returns_async_fetchxml_query(self, async_client): + """fetchxml() returns an AsyncFetchXmlQuery for valid XML.""" + q = async_client.query.fetchxml(_SIMPLE_FETCHXML) + assert isinstance(q, AsyncFetchXmlQuery) + assert q._entity_name == "account" + + +class TestAsyncQueryBuilder: + async def test_execute_returns_query_result(self, async_client, mock_od): + """builder().execute() collects all pages into a QueryResult.""" + + async def _pages(*args, **kwargs): + yield [{"name": "Contoso", "accountid": "g1"}] + yield [{"name": "Fabrikam", "accountid": "g2"}] + + mock_od._get_multiple = _pages + + result = await async_client.query.builder("account").select("name").execute() + + assert isinstance(result, QueryResult) + assert len(result) == 2 + assert result[0]["name"] == "Contoso" + assert result[1]["name"] == "Fabrikam" + + async def test_execute_pages_yields_per_page(self, async_client, mock_od): + """builder().execute_pages() yields one QueryResult per page.""" + + async def _pages(*args, **kwargs): + yield [{"name": "A", "accountid": "g1"}] + yield [{"name": "B", "accountid": "g2"}] + + mock_od._get_multiple = _pages + + pages = [] + async for page in async_client.query.builder("account").select("name").execute_pages(): + pages.append(page) + + assert len(pages) == 2 + assert pages[0][0]["name"] == "A" + assert pages[1][0]["name"] == "B" + + async def test_execute_raises_without_scope(self, async_client): + """execute() raises ValueError when no select/where/top/page_size is set.""" + with pytest.raises(ValueError, match="full-table scans"): + await async_client.query.builder("account").execute() + + async def test_execute_raises_when_unbound(self): + """execute() raises RuntimeError when builder was not created via client.query.builder().""" + qb = AsyncQueryBuilder("account") + qb.select("name") + with pytest.raises(RuntimeError, match="client.query.builder"): + await qb.execute() + + async def test_execute_pages_raises_without_scope(self, async_client): + """execute_pages() raises ValueError when no scope constraint is set.""" + with pytest.raises(ValueError, match="full-table scans"): + async for _ in async_client.query.builder("account").execute_pages(): + pass + + def test_chaining_methods_return_self(self, async_client): + """All fluent methods return the same AsyncQueryBuilder instance.""" + from PowerPlatform.Dataverse.models.filters import col + + qb = async_client.query.builder("account") + assert qb.select("name") is qb + assert qb.where(col("statecode") == 0) is qb + assert qb.order_by("name") is qb + assert qb.top(10) is qb + assert qb.page_size(5) is qb + + +class TestAsyncFetchXmlQueryFactory: + def test_fetchxml_invalid_type_raises(self, async_client): + """fetchxml() raises ValidationError when xml is not a string.""" + from PowerPlatform.Dataverse.core.errors import ValidationError + + with pytest.raises(ValidationError): + async_client.query.fetchxml(123) + + def test_fetchxml_empty_raises(self, async_client): + """fetchxml() raises ValidationError for empty string.""" + from PowerPlatform.Dataverse.core.errors import ValidationError + + with pytest.raises(ValidationError): + async_client.query.fetchxml(" ") + + def test_fetchxml_malformed_raises(self, async_client): + """fetchxml() raises ValidationError for malformed XML.""" + from PowerPlatform.Dataverse.core.errors import ValidationError + + with pytest.raises(ValidationError, match="not well-formed"): + async_client.query.fetchxml("") + + def test_fetchxml_missing_entity_element_raises(self, async_client): + """fetchxml() raises ValueError when element is absent.""" + with pytest.raises(ValueError, match=""): + async_client.query.fetchxml("") + + def test_fetchxml_missing_entity_name_raises(self, async_client): + """fetchxml() raises ValueError when has no name attribute.""" + with pytest.raises(ValueError, match="name"): + async_client.query.fetchxml("") + + +class TestAsyncFetchXmlQueryExecution: + async def test_execute_returns_query_result(self, async_client, mock_od): + """AsyncFetchXmlQuery.execute() collects all pages into a QueryResult.""" + mock_od._entity_set_from_schema_name = AsyncMock(return_value="accounts") + + resp = MagicMock() + resp.json = MagicMock( + return_value={ + "value": [{"name": "Contoso", "accountid": "g1"}], + "@Microsoft.Dynamics.CRM.morerecords": False, + } + ) + mock_od._request = AsyncMock(return_value=resp) + + result = await async_client.query.fetchxml(_SIMPLE_FETCHXML).execute() + + assert isinstance(result, QueryResult) + assert len(result) == 1 + assert result[0]["name"] == "Contoso" + + async def test_execute_pages_yields_pages(self, async_client, mock_od): + """AsyncFetchXmlQuery.execute_pages() yields one QueryResult per page.""" + mock_od._entity_set_from_schema_name = AsyncMock(return_value="accounts") + + resp = MagicMock() + resp.json = MagicMock( + return_value={ + "value": [{"name": "Contoso", "accountid": "g1"}], + "@Microsoft.Dynamics.CRM.morerecords": False, + } + ) + mock_od._request = AsyncMock(return_value=resp) + + pages = [] + async for page in async_client.query.fetchxml(_SIMPLE_FETCHXML).execute_pages(): + pages.append(page) + + assert len(pages) == 1 + assert pages[0][0]["name"] == "Contoso" + + +class TestAsyncQuerySql: + async def test_sql_returns_records(self, async_client, mock_od): + """sql() calls _query_sql and wraps results in Record objects.""" + mock_od._query_sql.return_value = [ + {"name": "Contoso", "accountid": "guid-1"}, + {"name": "Fabrikam", "accountid": "guid-2"}, + ] + + result = await async_client.query.sql("SELECT TOP 2 name FROM account") + + mock_od._query_sql.assert_called_once_with("SELECT TOP 2 name FROM account") + assert len(result) == 2 + assert all(isinstance(r, Record) for r in result) + assert result[0]["name"] == "Contoso" + assert result[1]["name"] == "Fabrikam" + + async def test_sql_empty_result(self, async_client, mock_od): + """sql() returns an empty list when no rows match.""" + mock_od._query_sql.return_value = [] + result = await async_client.query.sql("SELECT name FROM account WHERE name = 'X'") + assert result == [] + + +class TestAsyncQuerySqlColumns: + async def test_sql_columns_filters_virtual_and_system(self, async_client, mock_od): + """sql_columns() calls tables.list_columns and filters out virtual/system columns.""" + mock_od._list_columns.return_value = [ + { + "LogicalName": "name", + "AttributeType": "String", + "IsPrimaryId": False, + "IsPrimaryName": True, + "DisplayName": {}, + "AttributeOf": None, + }, + { + "LogicalName": "accountid", + "AttributeType": "Uniqueidentifier", + "IsPrimaryId": True, + "IsPrimaryName": False, + "DisplayName": {}, + "AttributeOf": None, + }, + { + "LogicalName": "versionnumber", + "AttributeType": "BigInt", + "IsPrimaryId": False, + "IsPrimaryName": False, + "DisplayName": {}, + "AttributeOf": None, + }, + ] + + cols = await async_client.query.sql_columns("account") + + # versionnumber is a system column — excluded by default + names = [c["name"] for c in cols] + assert "versionnumber" not in names + assert "accountid" in names + assert "name" in names + + async def test_sql_columns_include_system(self, async_client, mock_od): + """sql_columns(include_system=True) includes system columns.""" + mock_od._list_columns.return_value = [ + { + "LogicalName": "versionnumber", + "AttributeType": "BigInt", + "IsPrimaryId": False, + "IsPrimaryName": False, + "DisplayName": {}, + "AttributeOf": None, + } + ] + + cols = await async_client.query.sql_columns("account", include_system=True) + assert any(c["name"] == "versionnumber" for c in cols) + + async def test_sql_columns_excludes_attribute_of(self, async_client, mock_od): + """sql_columns() excludes columns where AttributeOf is set.""" + mock_od._list_columns.return_value = [ + { + "LogicalName": "parentcustomeridname", + "AttributeType": "String", + "IsPrimaryId": False, + "IsPrimaryName": False, + "DisplayName": {}, + "AttributeOf": "parentcustomerid", + } + ] + + cols = await async_client.query.sql_columns("contact") + assert cols == [] + + async def test_sql_columns_skips_empty_logical_name(self, async_client, mock_od): + """sql_columns() skips columns where LogicalName is empty.""" + mock_od._list_columns.return_value = [ + { + "LogicalName": "", + "AttributeType": "String", + "IsPrimaryId": False, + "IsPrimaryName": False, + "DisplayName": {}, + "AttributeOf": None, + }, + { + "LogicalName": "name", + "AttributeType": "String", + "IsPrimaryId": False, + "IsPrimaryName": True, + "DisplayName": {}, + "AttributeOf": None, + }, + ] + cols = await async_client.query.sql_columns("account") + names = [c["name"] for c in cols] + assert "" not in names + assert "name" in names + + async def test_sql_columns_extracts_display_label(self, async_client, mock_od): + """sql_columns() extracts label from UserLocalizedLabel when present.""" + mock_od._list_columns.return_value = [ + { + "LogicalName": "name", + "AttributeType": "String", + "IsPrimaryId": False, + "IsPrimaryName": True, + "DisplayName": {"UserLocalizedLabel": {"Label": "Account Name", "LanguageCode": 1033}}, + "AttributeOf": None, + }, + ] + cols = await async_client.query.sql_columns("account") + assert len(cols) == 1 + assert cols[0]["label"] == "Account Name" + + +class TestAsyncQueryOdataExpands: + async def test_odata_expands_returns_nav_properties(self, async_client, mock_od): + """odata_expands() returns navigation property metadata.""" + mock_od._list_table_relationships.return_value = [ + { + "ReferencingEntity": "contact", + "ReferencingEntityNavigationPropertyName": "parentcustomerid_account", + "ReferencedEntity": "account", + "ReferencingAttribute": "parentcustomerid", + "SchemaName": "contact_customer_accounts", + } + ] + mock_od._entity_set_from_schema_name.return_value = "accounts" + + result = await async_client.query.odata_expands("contact") + + assert len(result) == 1 + assert result[0]["nav_property"] == "parentcustomerid_account" + assert result[0]["target_table"] == "account" + + async def test_odata_expands_filters_non_referencing(self, async_client, mock_od): + """odata_expands() skips relationships where ReferencingEntity != table.""" + mock_od._list_table_relationships.return_value = [ + { + "ReferencingEntity": "account", # not "contact" + "ReferencingEntityNavigationPropertyName": "ownerid_systemuser", + "ReferencedEntity": "systemuser", + "ReferencingAttribute": "ownerid", + "SchemaName": "account_owner_rel", + } + ] + mock_od._entity_set_from_schema_name.return_value = "systemusers" + + result = await async_client.query.odata_expands("contact") + assert result == [] + + async def test_odata_expands_skips_empty_nav_prop(self, async_client, mock_od): + """odata_expands() skips relationships with empty nav_prop or target.""" + mock_od._list_table_relationships.return_value = [ + { + "ReferencingEntity": "contact", + "ReferencingEntityNavigationPropertyName": "", # empty nav prop + "ReferencedEntity": "account", + "ReferencingAttribute": "parentcustomerid", + "SchemaName": "contact_customer_accounts", + } + ] + mock_od._entity_set_from_schema_name.return_value = "accounts" + + result = await async_client.query.odata_expands("contact") + assert result == [] + + async def test_odata_expands_handles_entity_set_resolution_failure(self, async_client, mock_od): + """odata_expands() sets target_entity_set to '' when resolution raises.""" + from PowerPlatform.Dataverse.core.errors import MetadataError + + mock_od._list_table_relationships.return_value = [ + { + "ReferencingEntity": "contact", + "ReferencingEntityNavigationPropertyName": "parentcustomerid_account", + "ReferencedEntity": "account", + "ReferencingAttribute": "parentcustomerid", + "SchemaName": "contact_customer_accounts", + } + ] + mock_od._entity_set_from_schema_name.side_effect = MetadataError("not found") + + result = await async_client.query.odata_expands("contact") + + assert len(result) == 1 + assert result[0]["target_entity_set"] == "" + + +class TestAsyncFetchXmlQueryFactoryUrlLength: + def test_fetchxml_url_too_long_raises(self, async_client): + """fetchxml() raises ValidationError when encoded XML exceeds the URL length limit.""" + from PowerPlatform.Dataverse.core.errors import ValidationError + + # Build XML long enough to exceed _MAX_URL_LENGTH when encoded + long_xml = '' + '' * 1200 + "" + with pytest.raises(ValidationError, match="URL length limit"): + async_client.query.fetchxml(long_xml) + + +class TestAsyncFetchXmlQueryPaging: + """Tests for multi-page FetchXML execution paths.""" + + async def test_execute_multi_page_with_cookie(self, async_client, mock_od): + """execute() follows paging cookies across multiple pages.""" + import urllib.parse + + mock_od._entity_set_from_schema_name = AsyncMock(return_value="accounts") + + inner = '' + encoded = urllib.parse.quote(urllib.parse.quote(inner)) + paging_cookie = f'' + + page1 = MagicMock() + page1.json = MagicMock( + return_value={ + "value": [{"name": "Contoso", "accountid": "g1"}], + "@Microsoft.Dynamics.CRM.morerecords": True, + "@Microsoft.Dynamics.CRM.fetchxmlpagingcookie": paging_cookie, + } + ) + page2 = MagicMock() + page2.json = MagicMock( + return_value={ + "value": [{"name": "Fabrikam", "accountid": "g2"}], + "@Microsoft.Dynamics.CRM.morerecords": False, + } + ) + mock_od._request = AsyncMock(side_effect=[page1, page2]) + + result = await async_client.query.fetchxml(_SIMPLE_FETCHXML).execute() + + assert len(result) == 2 + assert result[0]["name"] == "Contoso" + assert result[1]["name"] == "Fabrikam" + + async def test_execute_multi_page_cookie_parse_error_fallback(self, async_client, mock_od): + """execute() falls back to simple paging when the cookie XML is malformed.""" + import warnings + + mock_od._entity_set_from_schema_name = AsyncMock(return_value="accounts") + + page1 = MagicMock() + page1.json = MagicMock( + return_value={ + "value": [{"name": "Contoso", "accountid": "g1"}], + "@Microsoft.Dynamics.CRM.morerecords": True, + "@Microsoft.Dynamics.CRM.fetchxmlpagingcookie": "<<>>", + } + ) + page2 = MagicMock() + page2.json = MagicMock( + return_value={ + "value": [{"name": "Fabrikam", "accountid": "g2"}], + "@Microsoft.Dynamics.CRM.morerecords": False, + } + ) + mock_od._request = AsyncMock(side_effect=[page1, page2]) + + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + result = await async_client.query.fetchxml(_SIMPLE_FETCHXML).execute() + + assert len(result) == 2 + assert any("paging cookie could not be parsed" in str(warning.message) for warning in w) + + async def test_execute_multi_page_no_cookie_simple_paging(self, async_client, mock_od): + """execute() falls back to simple page-number paging when no cookie is returned.""" + import warnings + + mock_od._entity_set_from_schema_name = AsyncMock(return_value="accounts") + + page1 = MagicMock() + page1.json = MagicMock( + return_value={ + "value": [{"name": "Contoso", "accountid": "g1"}], + "@Microsoft.Dynamics.CRM.morerecords": True, + # No fetchxmlpagingcookie key + } + ) + page2 = MagicMock() + page2.json = MagicMock( + return_value={ + "value": [{"name": "Fabrikam", "accountid": "g2"}], + "@Microsoft.Dynamics.CRM.morerecords": False, + } + ) + mock_od._request = AsyncMock(side_effect=[page1, page2]) + + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + result = await async_client.query.fetchxml(_SIMPLE_FETCHXML).execute() + + assert len(result) == 2 + assert any("simple paging" in str(warning.message) for warning in w) + + async def test_execute_json_parse_error_yields_empty_page(self, async_client, mock_od): + """execute() yields an empty page when the response body cannot be parsed as JSON.""" + mock_od._entity_set_from_schema_name = AsyncMock(return_value="accounts") + + resp = MagicMock() + resp.json = MagicMock(side_effect=Exception("invalid json")) + mock_od._request = AsyncMock(return_value=resp) + + result = await async_client.query.fetchxml(_SIMPLE_FETCHXML).execute() + assert len(result) == 0 + + async def test_execute_raises_on_max_pages_exceeded(self, async_client, mock_od): + """execute() raises ValidationError when paging exceeds the maximum page limit.""" + import urllib.parse + import warnings + from PowerPlatform.Dataverse.core.errors import ValidationError + + mock_od._entity_set_from_schema_name = AsyncMock(return_value="accounts") + + def _make_page_resp(page_num: int): + inner = f'' + encoded = urllib.parse.quote(urllib.parse.quote(inner)) + cookie = f'' + resp = MagicMock() + resp.json = MagicMock( + return_value={ + "value": [{"name": f"Record{page_num}", "accountid": f"g{page_num}"}], + "@Microsoft.Dynamics.CRM.morerecords": True, + "@Microsoft.Dynamics.CRM.fetchxmlpagingcookie": cookie, + } + ) + return resp + + # Always return morerecords=True to trigger the limit + mock_od._request = AsyncMock(side_effect=lambda *a, **kw: _make_page_resp(1)) + + with warnings.catch_warnings(record=True): + warnings.simplefilter("always") + with pytest.raises(ValidationError, match="exceeded"): + await async_client.query.fetchxml(_SIMPLE_FETCHXML).execute() diff --git a/tests/unit/aio/test_async_records.py b/tests/unit/aio/test_async_records.py new file mode 100644 index 00000000..13b72582 --- /dev/null +++ b/tests/unit/aio/test_async_records.py @@ -0,0 +1,541 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +import warnings +import pytest +from unittest.mock import MagicMock + +from PowerPlatform.Dataverse.aio.operations.async_records import AsyncRecordOperations +from PowerPlatform.Dataverse.core.errors import HttpError +from PowerPlatform.Dataverse.models.record import QueryResult, Record +from PowerPlatform.Dataverse.models.upsert import UpsertItem + +# --------------------------------------------------------------------------- +# Async generator helpers used by list/list_pages tests +# --------------------------------------------------------------------------- + + +async def _agen(*pages): + """Yield each argument as one page from an async generator.""" + for p in pages: + yield p + + +class TestAsyncRecordOperationsNamespace: + """Verify the namespace attribute type.""" + + def test_namespace_type(self, async_client): + assert isinstance(async_client.records, AsyncRecordOperations) + + +class TestAsyncRecordCreate: + """Tests for AsyncRecordOperations.create.""" + + async def test_create_single(self, async_client, mock_od): + """create() with a single dict calls _entity_set_from_schema_name and _create.""" + mock_od._entity_set_from_schema_name.return_value = "accounts" + mock_od._create.return_value = "guid-123" + + result = await async_client.records.create("account", {"name": "Contoso"}) + + mock_od._entity_set_from_schema_name.assert_called_once_with("account") + mock_od._create.assert_called_once_with("accounts", "account", {"name": "Contoso"}) + assert result == "guid-123" + assert isinstance(result, str) + + async def test_create_bulk(self, async_client, mock_od): + """create() with a list of dicts calls _create_multiple.""" + payloads = [{"name": "A"}, {"name": "B"}] + mock_od._entity_set_from_schema_name.return_value = "accounts" + mock_od._create_multiple.return_value = ["guid-1", "guid-2"] + + result = await async_client.records.create("account", payloads) + + mock_od._create_multiple.assert_called_once_with("accounts", "account", payloads) + assert result == ["guid-1", "guid-2"] + + async def test_create_single_non_string_return_raises(self, async_client, mock_od): + """create() raises TypeError if _create returns a non-string.""" + mock_od._entity_set_from_schema_name.return_value = "accounts" + mock_od._create.return_value = 12345 + + with pytest.raises(TypeError): + await async_client.records.create("account", {"name": "X"}) + + async def test_create_bulk_non_list_return_raises(self, async_client, mock_od): + """create() raises TypeError if _create_multiple returns a non-list.""" + mock_od._entity_set_from_schema_name.return_value = "accounts" + mock_od._create_multiple.return_value = "not-a-list" + + with pytest.raises(TypeError): + await async_client.records.create("account", [{"name": "X"}]) + + async def test_create_invalid_data_type_raises(self, async_client, mock_od): + """create() raises TypeError if data is neither dict nor list.""" + mock_od._entity_set_from_schema_name.return_value = "accounts" + with pytest.raises(TypeError): + await async_client.records.create("account", "invalid") + + +class TestAsyncRecordUpdate: + """Tests for AsyncRecordOperations.update.""" + + async def test_update_single(self, async_client, mock_od): + """update() with a str id and dict changes calls _update.""" + await async_client.records.update("account", "guid-1", {"telephone1": "555"}) + mock_od._update.assert_called_once_with("account", "guid-1", {"telephone1": "555"}) + + async def test_update_broadcast(self, async_client, mock_od): + """update() with list of ids and single dict calls _update_by_ids.""" + await async_client.records.update("account", ["id-1", "id-2"], {"statecode": 1}) + mock_od._update_by_ids.assert_called_once_with("account", ["id-1", "id-2"], {"statecode": 1}) + + async def test_update_paired(self, async_client, mock_od): + """update() with list of ids and list of dicts calls _update_by_ids.""" + await async_client.records.update("account", ["id-1", "id-2"], [{"name": "A"}, {"name": "B"}]) + mock_od._update_by_ids.assert_called_once_with("account", ["id-1", "id-2"], [{"name": "A"}, {"name": "B"}]) + + async def test_update_single_non_dict_changes_raises(self, async_client, mock_od): + """update() raises TypeError if ids is str but changes is not a dict.""" + with pytest.raises(TypeError): + await async_client.records.update("account", "guid-1", ["not", "a", "dict"]) + + async def test_update_invalid_ids_type_raises(self, async_client, mock_od): + """update() raises TypeError if ids is neither str nor list.""" + with pytest.raises(TypeError): + await async_client.records.update("account", 12345, {"name": "X"}) + + async def test_update_returns_none(self, async_client, mock_od): + """update() returns None.""" + result = await async_client.records.update("account", "guid-1", {"name": "X"}) + assert result is None + + +class TestAsyncRecordDelete: + """Tests for AsyncRecordOperations.delete.""" + + async def test_delete_single(self, async_client, mock_od): + """delete() with a str id calls _delete and returns None.""" + result = await async_client.records.delete("account", "guid-to-delete") + mock_od._delete.assert_called_once_with("account", "guid-to-delete") + assert result is None + + async def test_delete_bulk(self, async_client, mock_od): + """delete() with a list of ids uses _delete_multiple by default.""" + mock_od._delete_multiple.return_value = "job-guid-456" + result = await async_client.records.delete("account", ["id-1", "id-2", "id-3"]) + mock_od._delete_multiple.assert_called_once_with("account", ["id-1", "id-2", "id-3"]) + assert result == "job-guid-456" + + async def test_delete_bulk_sequential(self, async_client, mock_od): + """delete() with use_bulk_delete=False calls _delete once per id.""" + result = await async_client.records.delete("account", ["id-1", "id-2"], use_bulk_delete=False) + assert mock_od._delete.call_count == 2 + mock_od._delete.assert_any_call("account", "id-1") + mock_od._delete.assert_any_call("account", "id-2") + mock_od._delete_multiple.assert_not_called() + assert result is None + + async def test_delete_empty_list(self, async_client, mock_od): + """delete() with an empty list returns None without calling _delete.""" + result = await async_client.records.delete("account", []) + mock_od._delete.assert_not_called() + mock_od._delete_multiple.assert_not_called() + assert result is None + + async def test_delete_invalid_ids_type_raises(self, async_client, mock_od): + """delete() raises TypeError if ids is neither str nor list.""" + with pytest.raises(TypeError): + await async_client.records.delete("account", 12345) + + async def test_delete_list_with_non_string_guid_raises(self, async_client, mock_od): + """delete() raises TypeError if the ids list contains non-string entries.""" + with pytest.raises(TypeError): + await async_client.records.delete("account", ["valid-guid", 42]) + + +class TestAsyncRecordUpsert: + """Tests for AsyncRecordOperations.upsert.""" + + async def test_upsert_single_upsert_item(self, async_client, mock_od): + """upsert() with a single UpsertItem calls _upsert.""" + mock_od._entity_set_from_schema_name.return_value = "accounts" + item = UpsertItem(alternate_key={"accountnumber": "ACC-001"}, record={"name": "Contoso"}) + + result = await async_client.records.upsert("account", [item]) + + mock_od._upsert.assert_called_once_with( + "accounts", "account", {"accountnumber": "ACC-001"}, {"name": "Contoso"} + ) + mock_od._upsert_multiple.assert_not_called() + assert result is None + + async def test_upsert_single_dict(self, async_client, mock_od): + """upsert() with a single dict item calls _upsert.""" + mock_od._entity_set_from_schema_name.return_value = "accounts" + item = {"alternate_key": {"accountnumber": "ACC-001"}, "record": {"name": "Contoso"}} + + await async_client.records.upsert("account", [item]) + + mock_od._upsert.assert_called_once_with( + "accounts", "account", {"accountnumber": "ACC-001"}, {"name": "Contoso"} + ) + + async def test_upsert_multiple_calls_upsert_multiple(self, async_client, mock_od): + """upsert() with multiple items calls _upsert_multiple.""" + mock_od._entity_set_from_schema_name.return_value = "accounts" + items = [ + UpsertItem(alternate_key={"accountnumber": "A"}, record={"name": "Contoso"}), + UpsertItem(alternate_key={"accountnumber": "B"}, record={"name": "Fabrikam"}), + ] + + await async_client.records.upsert("account", items) + + mock_od._upsert_multiple.assert_called_once_with( + "accounts", + "account", + [{"accountnumber": "A"}, {"accountnumber": "B"}], + [{"name": "Contoso"}, {"name": "Fabrikam"}], + ) + mock_od._upsert.assert_not_called() + + async def test_upsert_empty_list_raises(self, async_client, mock_od): + """upsert() with an empty list raises TypeError.""" + with pytest.raises(TypeError): + await async_client.records.upsert("account", []) + + async def test_upsert_non_list_raises(self, async_client, mock_od): + """upsert() with a non-list argument raises TypeError.""" + item = UpsertItem(alternate_key={"accountnumber": "X"}, record={"name": "Y"}) + with pytest.raises(TypeError): + await async_client.records.upsert("account", item) + + async def test_upsert_invalid_item_raises(self, async_client, mock_od): + """upsert() with an item that is neither UpsertItem nor valid dict raises TypeError.""" + with pytest.raises(TypeError): + await async_client.records.upsert("account", [42]) + + async def test_upsert_dict_missing_record_key_raises(self, async_client, mock_od): + """upsert() with a dict missing the 'record' key raises TypeError.""" + with pytest.raises(TypeError): + await async_client.records.upsert("account", [{"alternate_key": {"name": "acc1"}}]) + + +# --------------------------------------------------------------------------- +# retrieve() +# --------------------------------------------------------------------------- + + +class TestAsyncRecordRetrieve: + """Tests for AsyncRecordOperations.retrieve().""" + + async def test_retrieve_returns_record(self, async_client, mock_od): + """retrieve() returns a Record instance.""" + mock_od._get.return_value = {"accountid": "abc", "name": "Contoso"} + result = await async_client.records.retrieve("account", "abc") + assert isinstance(result, Record) + assert result["name"] == "Contoso" + + async def test_retrieve_passes_select(self, async_client, mock_od): + """retrieve() passes select= to _get.""" + mock_od._get.return_value = {"accountid": "abc", "name": "Contoso"} + await async_client.records.retrieve("account", "abc", select=["name"]) + mock_od._get.assert_called_once_with("account", "abc", select=["name"], expand=None, include_annotations=None) + + async def test_retrieve_passes_expand(self, async_client, mock_od): + """retrieve() passes expand= to _get.""" + mock_od._get.return_value = { + "accountid": "abc", + "primarycontactid": {"contactid": "cid", "fullname": "John Doe"}, + } + result = await async_client.records.retrieve("account", "abc", expand=["primarycontactid"]) + mock_od._get.assert_called_once_with( + "account", "abc", select=None, expand=["primarycontactid"], include_annotations=None + ) + assert result["primarycontactid"]["fullname"] == "John Doe" + + async def test_retrieve_passes_select_and_expand(self, async_client, mock_od): + """retrieve() passes both select= and expand= to _get.""" + mock_od._get.return_value = {"name": "Contoso", "primarycontactid": {"fullname": "John"}} + await async_client.records.retrieve("account", "abc", select=["name"], expand=["primarycontactid"]) + mock_od._get.assert_called_once_with( + "account", "abc", select=["name"], expand=["primarycontactid"], include_annotations=None + ) + + async def test_retrieve_passes_include_annotations(self, async_client, mock_od): + """retrieve() passes include_annotations= to _get.""" + annotation = "OData.Community.Display.V1.FormattedValue" + mock_od._get.return_value = { + "accountid": "abc", + "statuscode": 1, + f"statuscode@{annotation}": "Active", + } + result = await async_client.records.retrieve("account", "abc", include_annotations=annotation) + mock_od._get.assert_called_once_with("account", "abc", select=None, expand=None, include_annotations=annotation) + assert result[f"statuscode@{annotation}"] == "Active" + + async def test_retrieve_no_deprecation_warning(self, async_client, mock_od): + """retrieve() does not emit DeprecationWarning.""" + mock_od._get.return_value = {"accountid": "abc", "name": "Contoso"} + with warnings.catch_warnings(record=True) as caught: + warnings.simplefilter("always") + await async_client.records.retrieve("account", "abc") + dep = [w for w in caught if issubclass(w.category, DeprecationWarning)] + assert dep == [], f"retrieve() must not emit DeprecationWarning: {dep}" + + async def test_retrieve_returns_none_on_404(self, async_client, mock_od): + """retrieve() returns None when _get raises HttpError with status 404.""" + mock_od._get.side_effect = HttpError("Not Found", 404) + result = await async_client.records.retrieve("account", "nonexistent") + assert result is None + + async def test_retrieve_reraises_non_404(self, async_client, mock_od): + """retrieve() re-raises HttpError for non-404 status codes.""" + mock_od._get.side_effect = HttpError("Server Error", 500) + with pytest.raises(HttpError): + await async_client.records.retrieve("account", "some-id") + + async def test_retrieve_reraises_non_http_errors(self, async_client, mock_od): + """retrieve() re-raises non-HttpError exceptions unchanged.""" + mock_od._get.side_effect = ValueError("Bad input") + with pytest.raises(ValueError): + await async_client.records.retrieve("account", "some-id") + + async def test_retrieve_record_id_set(self, async_client, mock_od): + """retrieve() sets record.id from the record_id argument.""" + mock_od._get.return_value = {"name": "Contoso"} + record = await async_client.records.retrieve("account", "my-id") + assert record.id == "my-id" + + async def test_retrieve_table_set(self, async_client, mock_od): + """retrieve() sets record.table from the table argument.""" + mock_od._get.return_value = {"name": "Contoso"} + record = await async_client.records.retrieve("account", "my-id") + assert record.table == "account" + + +# --------------------------------------------------------------------------- +# list() +# --------------------------------------------------------------------------- + + +class TestAsyncRecordList: + """Tests for AsyncRecordOperations.list().""" + + async def test_list_returns_query_result(self, async_client, mock_od): + """list() returns a QueryResult.""" + mock_od._get_multiple = MagicMock(return_value=_agen()) + result = await async_client.records.list("account") + assert isinstance(result, QueryResult) + + async def test_list_collects_all_pages(self, async_client, mock_od): + """list() collects records from all pages into one QueryResult.""" + mock_od._get_multiple = MagicMock( + return_value=_agen( + [{"name": "A", "accountid": "1"}], + [{"name": "B", "accountid": "2"}, {"name": "C", "accountid": "3"}], + ) + ) + result = await async_client.records.list("account") + assert len(result) == 3 + + async def test_list_no_deprecation_warning(self, async_client, mock_od): + """list() does not emit DeprecationWarning.""" + mock_od._get_multiple = MagicMock(return_value=_agen()) + with warnings.catch_warnings(record=True) as caught: + warnings.simplefilter("always") + await async_client.records.list("account", filter="statecode eq 0") + dep = [w for w in caught if issubclass(w.category, DeprecationWarning)] + assert dep == [], f"list() must not emit DeprecationWarning: {dep}" + + async def test_list_passes_string_filter(self, async_client, mock_od): + """list() passes a string filter to _get_multiple.""" + mock_od._get_multiple = MagicMock(return_value=_agen()) + await async_client.records.list("account", filter="statecode eq 0") + assert mock_od._get_multiple.call_args[1]["filter"] == "statecode eq 0" + + async def test_list_passes_filter_expression(self, async_client, mock_od): + """list() converts a FilterExpression to string before passing to _get_multiple.""" + from PowerPlatform.Dataverse.models.filters import col + + mock_od._get_multiple = MagicMock(return_value=_agen()) + await async_client.records.list("account", filter=col("statecode") == 0) + assert mock_od._get_multiple.call_args[1]["filter"] == "statecode eq 0" + + async def test_list_passes_select(self, async_client, mock_od): + """list() passes select= to _get_multiple.""" + mock_od._get_multiple = MagicMock(return_value=_agen()) + await async_client.records.list("account", select=["name", "revenue"]) + assert mock_od._get_multiple.call_args[1]["select"] == ["name", "revenue"] + + async def test_list_passes_top(self, async_client, mock_od): + """list() passes top= to _get_multiple.""" + mock_od._get_multiple = MagicMock(return_value=_agen()) + await async_client.records.list("account", top=50) + assert mock_od._get_multiple.call_args[1]["top"] == 50 + + async def test_list_none_filter_passes_none(self, async_client, mock_od): + """list() passes filter=None to _get_multiple when no filter specified.""" + mock_od._get_multiple = MagicMock(return_value=_agen()) + await async_client.records.list("account") + assert mock_od._get_multiple.call_args[1]["filter"] is None + + async def test_list_result_iterable(self, async_client, mock_od): + """list() result is iterable and contains Record instances.""" + mock_od._get_multiple = MagicMock(return_value=_agen([{"name": "X", "accountid": "1"}])) + result = await async_client.records.list("account") + records = list(result) + assert len(records) == 1 + assert records[0]["name"] == "X" + + async def test_list_result_to_dataframe(self, async_client, mock_od): + """list() result can be converted to a DataFrame.""" + import pandas as pd + + mock_od._get_multiple = MagicMock( + return_value=_agen([{"name": "A", "accountid": "1"}, {"name": "B", "accountid": "2"}]) + ) + df = (await async_client.records.list("account", select=["name"])).to_dataframe() + assert isinstance(df, pd.DataFrame) + assert len(df) == 2 + + async def test_list_passes_orderby(self, async_client, mock_od): + """list() passes orderby= to _get_multiple.""" + mock_od._get_multiple = MagicMock(return_value=_agen()) + await async_client.records.list("account", orderby=["name asc"]) + assert mock_od._get_multiple.call_args[1]["orderby"] == ["name asc"] + + async def test_list_passes_expand(self, async_client, mock_od): + """list() passes expand= to _get_multiple.""" + mock_od._get_multiple = MagicMock(return_value=_agen()) + await async_client.records.list("account", expand=["primarycontactid"]) + assert mock_od._get_multiple.call_args[1]["expand"] == ["primarycontactid"] + + async def test_list_passes_page_size(self, async_client, mock_od): + """list() passes page_size= to _get_multiple.""" + mock_od._get_multiple = MagicMock(return_value=_agen()) + await async_client.records.list("account", page_size=200) + assert mock_od._get_multiple.call_args[1]["page_size"] == 200 + + async def test_list_passes_count(self, async_client, mock_od): + """list() passes count=True to _get_multiple.""" + mock_od._get_multiple = MagicMock(return_value=_agen()) + await async_client.records.list("account", count=True) + assert mock_od._get_multiple.call_args[1]["count"] is True + + async def test_list_passes_include_annotations(self, async_client, mock_od): + """list() passes include_annotations= to _get_multiple.""" + annotation = "OData.Community.Display.V1.FormattedValue" + mock_od._get_multiple = MagicMock(return_value=_agen()) + await async_client.records.list("account", include_annotations=annotation) + assert mock_od._get_multiple.call_args[1]["include_annotations"] == annotation + + +# --------------------------------------------------------------------------- +# list_pages() +# --------------------------------------------------------------------------- + + +class TestAsyncRecordListPages: + """Tests for AsyncRecordOperations.list_pages().""" + + async def test_list_pages_is_async_generator(self, async_client, mock_od): + """list_pages() returns an async generator.""" + import inspect + + mock_od._get_multiple = MagicMock(return_value=_agen()) + result = async_client.records.list_pages("account") + assert inspect.isasyncgen(result) + + async def test_list_pages_yields_query_result_per_page(self, async_client, mock_od): + """list_pages() yields one QueryResult per HTTP page.""" + mock_od._get_multiple = MagicMock( + return_value=_agen([{"name": "A", "accountid": "1"}], [{"name": "B", "accountid": "2"}]) + ) + pages = [] + async for page in async_client.records.list_pages("account"): + pages.append(page) + assert len(pages) == 2 + for page in pages: + assert isinstance(page, QueryResult) + + async def test_list_pages_page_contents(self, async_client, mock_od): + """list_pages() preserves per-page record counts.""" + mock_od._get_multiple = MagicMock( + return_value=_agen( + [{"name": "A", "accountid": "1"}], + [{"name": "B", "accountid": "2"}, {"name": "C", "accountid": "3"}], + ) + ) + pages = [] + async for page in async_client.records.list_pages("account"): + pages.append(page) + assert len(pages[0]) == 1 + assert len(pages[1]) == 2 + + async def test_list_pages_no_deprecation_warning(self, async_client, mock_od): + """list_pages() does not emit DeprecationWarning.""" + mock_od._get_multiple = MagicMock(return_value=_agen()) + with warnings.catch_warnings(record=True) as caught: + warnings.simplefilter("always") + async for _ in async_client.records.list_pages("account", filter="statecode eq 0"): + pass + dep = [w for w in caught if issubclass(w.category, DeprecationWarning)] + assert dep == [], f"list_pages() must not emit DeprecationWarning: {dep}" + + async def test_list_pages_passes_filter(self, async_client, mock_od): + """list_pages() passes filter= to _get_multiple.""" + mock_od._get_multiple = MagicMock(return_value=_agen()) + async for _ in async_client.records.list_pages("account", filter="statecode eq 0"): + pass + assert mock_od._get_multiple.call_args[1]["filter"] == "statecode eq 0" + + async def test_list_pages_passes_select(self, async_client, mock_od): + """list_pages() passes select= to _get_multiple.""" + mock_od._get_multiple = MagicMock(return_value=_agen()) + async for _ in async_client.records.list_pages("account", select=["name"]): + pass + assert mock_od._get_multiple.call_args[1]["select"] == ["name"] + + async def test_list_pages_passes_top(self, async_client, mock_od): + """list_pages() passes top= to _get_multiple.""" + mock_od._get_multiple = MagicMock(return_value=_agen()) + async for _ in async_client.records.list_pages("account", top=50): + pass + assert mock_od._get_multiple.call_args[1]["top"] == 50 + + async def test_list_pages_passes_orderby(self, async_client, mock_od): + """list_pages() passes orderby= to _get_multiple.""" + mock_od._get_multiple = MagicMock(return_value=_agen()) + async for _ in async_client.records.list_pages("account", orderby=["name asc"]): + pass + assert mock_od._get_multiple.call_args[1]["orderby"] == ["name asc"] + + async def test_list_pages_passes_expand(self, async_client, mock_od): + """list_pages() passes expand= to _get_multiple.""" + mock_od._get_multiple = MagicMock(return_value=_agen()) + async for _ in async_client.records.list_pages("account", expand=["primarycontactid"]): + pass + assert mock_od._get_multiple.call_args[1]["expand"] == ["primarycontactid"] + + async def test_list_pages_passes_page_size(self, async_client, mock_od): + """list_pages() passes page_size= to _get_multiple.""" + mock_od._get_multiple = MagicMock(return_value=_agen()) + async for _ in async_client.records.list_pages("account", page_size=200): + pass + assert mock_od._get_multiple.call_args[1]["page_size"] == 200 + + async def test_list_pages_passes_count(self, async_client, mock_od): + """list_pages() passes count=True to _get_multiple.""" + mock_od._get_multiple = MagicMock(return_value=_agen()) + async for _ in async_client.records.list_pages("account", count=True): + pass + assert mock_od._get_multiple.call_args[1]["count"] is True + + async def test_list_pages_passes_include_annotations(self, async_client, mock_od): + """list_pages() passes include_annotations= to _get_multiple.""" + annotation = "OData.Community.Display.V1.FormattedValue" + mock_od._get_multiple = MagicMock(return_value=_agen()) + async for _ in async_client.records.list_pages("account", include_annotations=annotation): + pass + assert mock_od._get_multiple.call_args[1]["include_annotations"] == annotation diff --git a/tests/unit/aio/test_async_tables.py b/tests/unit/aio/test_async_tables.py new file mode 100644 index 00000000..366f0c74 --- /dev/null +++ b/tests/unit/aio/test_async_tables.py @@ -0,0 +1,314 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + + +from PowerPlatform.Dataverse.aio.operations.async_tables import AsyncTableOperations +from PowerPlatform.Dataverse.models.relationship import RelationshipInfo +from PowerPlatform.Dataverse.models.table_info import AlternateKeyInfo, TableInfo +from PowerPlatform.Dataverse.models.relationship import ( + LookupAttributeMetadata, + OneToManyRelationshipMetadata, + ManyToManyRelationshipMetadata, +) +from PowerPlatform.Dataverse.models.labels import Label, LocalizedLabel + + +def _label(text: str = "Test") -> Label: + return Label(localized_labels=[LocalizedLabel(label=text, language_code=1033)]) + + +def _table_raw(schema_name: str = "new_Product") -> dict: + return { + "table_schema_name": schema_name, + "entity_set_name": "new_products", + "table_logical_name": "new_product", + "metadata_id": "meta-guid-1", + "columns_created": ["new_Price"], + } + + +def _rel_one_to_many_raw() -> dict: + return { + "relationship_id": "rel-guid-1", + "relationship_schema_name": "new_Dept_Emp", + "lookup_schema_name": "new_DeptId", + "referenced_entity": "new_dept", + "referencing_entity": "new_employee", + } + + +def _rel_many_to_many_raw() -> dict: + return { + "relationship_id": "rel-guid-2", + "relationship_schema_name": "new_emp_proj", + "entity1_logical_name": "new_employee", + "entity2_logical_name": "new_project", + } + + +class TestAsyncTableOperationsNamespace: + def test_namespace_type(self, async_client): + assert isinstance(async_client.tables, AsyncTableOperations) + + +class TestAsyncTableCreate: + async def test_create_returns_table_info(self, async_client, mock_od): + """create() returns a TableInfo built from the raw dict.""" + mock_od._create_table.return_value = _table_raw() + columns = {"new_Price": "decimal"} + + result = await async_client.tables.create( + "new_Product", + columns, + solution="MySol", + primary_column="new_ProductName", + display_name="Product", + ) + + mock_od._create_table.assert_called_once_with("new_Product", columns, "MySol", "new_ProductName", "Product") + assert isinstance(result, TableInfo) + assert result.schema_name == "new_Product" + + async def test_create_with_minimal_args(self, async_client, mock_od): + """create() works with only table and columns.""" + mock_od._create_table.return_value = _table_raw() + await async_client.tables.create("new_Product", {}) + mock_od._create_table.assert_called_once_with("new_Product", {}, None, None, None) + + +class TestAsyncTableDelete: + async def test_delete_calls_delete_table(self, async_client, mock_od): + """delete() calls _delete_table with the table schema name.""" + await async_client.tables.delete("new_Product") + mock_od._delete_table.assert_called_once_with("new_Product") + + +class TestAsyncTableGet: + async def test_get_returns_table_info(self, async_client, mock_od): + """get() returns TableInfo when table exists.""" + mock_od._get_table_info.return_value = _table_raw() + result = await async_client.tables.get("new_Product") + assert isinstance(result, TableInfo) + assert result.schema_name == "new_Product" + + async def test_get_returns_none_when_not_found(self, async_client, mock_od): + """get() returns None when _get_table_info returns None.""" + mock_od._get_table_info.return_value = None + result = await async_client.tables.get("new_Product") + assert result is None + + +class TestAsyncTableList: + async def test_list_calls_list_tables(self, async_client, mock_od): + """list() calls _list_tables and returns its result.""" + mock_od._list_tables.return_value = [{"LogicalName": "account"}] + result = await async_client.tables.list() + mock_od._list_tables.assert_called_once_with(filter=None, select=None) + assert result == [{"LogicalName": "account"}] + + async def test_list_with_params(self, async_client, mock_od): + """list() passes filter and select to _list_tables.""" + mock_od._list_tables.return_value = [] + await async_client.tables.list(filter="IsPrivate eq false", select=["LogicalName"]) + mock_od._list_tables.assert_called_once_with(filter="IsPrivate eq false", select=["LogicalName"]) + + +class TestAsyncTableAddColumns: + async def test_add_columns_calls_create_columns(self, async_client, mock_od): + """add_columns() calls _create_columns and returns the result.""" + mock_od._create_columns.return_value = ["new_Notes"] + result = await async_client.tables.add_columns("new_Product", {"new_Notes": "string"}) + mock_od._create_columns.assert_called_once_with("new_Product", {"new_Notes": "string"}) + assert result == ["new_Notes"] + + +class TestAsyncTableRemoveColumns: + async def test_remove_columns_calls_delete_columns(self, async_client, mock_od): + """remove_columns() calls _delete_columns and returns the result.""" + mock_od._delete_columns.return_value = ["new_Notes"] + result = await async_client.tables.remove_columns("new_Product", "new_Notes") + mock_od._delete_columns.assert_called_once_with("new_Product", "new_Notes") + assert result == ["new_Notes"] + + +class TestAsyncTableOneToManyRelationship: + async def test_create_one_to_many(self, async_client, mock_od): + """create_one_to_many_relationship() calls _create_one_to_many_relationship and returns RelationshipInfo.""" + mock_od._create_one_to_many_relationship.return_value = _rel_one_to_many_raw() + + lookup = LookupAttributeMetadata(schema_name="new_DeptId", display_name=_label("Department")) + relationship = OneToManyRelationshipMetadata( + schema_name="new_Dept_Emp", + referenced_entity="new_dept", + referencing_entity="new_employee", + referenced_attribute="new_deptid", + ) + + result = await async_client.tables.create_one_to_many_relationship(lookup, relationship) + + mock_od._create_one_to_many_relationship.assert_called_once_with(lookup, relationship, None) + assert isinstance(result, RelationshipInfo) + assert result.relationship_schema_name == "new_Dept_Emp" + + +class TestAsyncTableManyToManyRelationship: + async def test_create_many_to_many(self, async_client, mock_od): + """create_many_to_many_relationship() calls _create_many_to_many_relationship and returns RelationshipInfo.""" + mock_od._create_many_to_many_relationship.return_value = _rel_many_to_many_raw() + + relationship = ManyToManyRelationshipMetadata( + schema_name="new_emp_proj", + entity1_logical_name="new_employee", + entity2_logical_name="new_project", + ) + + result = await async_client.tables.create_many_to_many_relationship(relationship) + + mock_od._create_many_to_many_relationship.assert_called_once_with(relationship, None) + assert isinstance(result, RelationshipInfo) + assert result.relationship_schema_name == "new_emp_proj" + + +class TestAsyncTableDeleteRelationship: + async def test_delete_relationship(self, async_client, mock_od): + """delete_relationship() calls _delete_relationship with the relationship_id.""" + await async_client.tables.delete_relationship("rel-guid-1") + mock_od._delete_relationship.assert_called_once_with("rel-guid-1") + + +class TestAsyncTableGetRelationship: + async def test_get_relationship_found(self, async_client, mock_od): + """get_relationship() returns RelationshipInfo when found.""" + raw = { + "@odata.type": "#Microsoft.Dynamics.CRM.OneToManyRelationshipMetadata", + "RelationshipId": "rel-guid-1", + "SchemaName": "new_Dept_Emp", + "RelationshipType": "OneToManyRelationship", + "ReferencedEntity": "new_dept", + "ReferencingEntity": "new_employee", + "ReferencingAttribute": "new_deptid", + } + mock_od._get_relationship.return_value = raw + result = await async_client.tables.get_relationship("new_Dept_Emp") + assert isinstance(result, RelationshipInfo) + + async def test_get_relationship_not_found(self, async_client, mock_od): + """get_relationship() returns None when _get_relationship returns None.""" + mock_od._get_relationship.return_value = None + result = await async_client.tables.get_relationship("nonexistent") + assert result is None + + +class TestAsyncTableCreateLookupField: + async def test_create_lookup_field_builds_models_and_delegates(self, async_client, mock_od): + """create_lookup_field() builds lookup/relationship models and calls create_one_to_many_relationship.""" + from unittest.mock import MagicMock + + mock_lookup = LookupAttributeMetadata(schema_name="new_AccountId", display_name=_label("Account")) + mock_rel = OneToManyRelationshipMetadata( + schema_name="new_account_order", + referenced_entity="account", + referencing_entity="new_order", + referenced_attribute="accountid", + ) + # _build_lookup_field_models is a sync method on _ODataBase; use MagicMock so + # od._build_lookup_field_models(...) returns the tuple directly (not a coroutine). + mock_od._build_lookup_field_models = MagicMock(return_value=(mock_lookup, mock_rel)) + mock_od._create_one_to_many_relationship.return_value = { + "relationship_id": "r-guid", + "relationship_schema_name": "new_account_order", + "lookup_schema_name": "new_AccountId", + "referenced_entity": "account", + "referencing_entity": "new_order", + } + + result = await async_client.tables.create_lookup_field( + referencing_table="new_order", + lookup_field_name="new_AccountId", + referenced_table="account", + ) + + mock_od._build_lookup_field_models.assert_called_once() + mock_od._create_one_to_many_relationship.assert_called_once_with(mock_lookup, mock_rel, None) + assert isinstance(result, RelationshipInfo) + + +class TestAsyncTableAlternateKeys: + async def test_create_alternate_key(self, async_client, mock_od): + """create_alternate_key() calls _create_alternate_key and returns AlternateKeyInfo.""" + mock_od._create_alternate_key.return_value = { + "metadata_id": "key-guid", + "schema_name": "new_prod_key", + "key_attributes": ["new_productcode"], + } + + result = await async_client.tables.create_alternate_key( + "new_Product", + "new_prod_key", + ["new_productcode"], + display_name="Product Code", + ) + + mock_od._create_alternate_key.assert_called_once() + assert isinstance(result, AlternateKeyInfo) + assert result.schema_name == "new_prod_key" + assert result.status == "Pending" + + async def test_get_alternate_keys(self, async_client, mock_od): + """get_alternate_keys() calls _get_alternate_keys and returns list of AlternateKeyInfo.""" + mock_od._get_alternate_keys.return_value = [ + { + "MetadataId": "key-guid-1", + "SchemaName": "new_prod_key", + "KeyAttributes": ["new_productcode"], + "EntityKeyIndexStatus": "Active", + } + ] + + result = await async_client.tables.get_alternate_keys("new_Product") + + mock_od._get_alternate_keys.assert_called_once_with("new_Product") + assert len(result) == 1 + assert isinstance(result[0], AlternateKeyInfo) + + async def test_delete_alternate_key(self, async_client, mock_od): + """delete_alternate_key() calls _delete_alternate_key with table and key_id.""" + await async_client.tables.delete_alternate_key("new_Product", "key-guid") + mock_od._delete_alternate_key.assert_called_once_with("new_Product", "key-guid") + + +class TestAsyncTableListColumns: + async def test_list_columns(self, async_client, mock_od): + """list_columns() calls _list_columns and returns its result.""" + mock_od._list_columns.return_value = [{"LogicalName": "name"}] + result = await async_client.tables.list_columns("account") + mock_od._list_columns.assert_called_once_with("account", select=None, filter=None) + assert result == [{"LogicalName": "name"}] + + async def test_list_columns_with_params(self, async_client, mock_od): + """list_columns() passes select and filter to _list_columns.""" + mock_od._list_columns.return_value = [] + await async_client.tables.list_columns( + "account", + select=["LogicalName"], + filter="AttributeType eq 'String'", + ) + mock_od._list_columns.assert_called_once_with( + "account", select=["LogicalName"], filter="AttributeType eq 'String'" + ) + + +class TestAsyncTableListRelationships: + async def test_list_relationships(self, async_client, mock_od): + """list_relationships() calls _list_relationships and returns its result.""" + mock_od._list_relationships.return_value = [{"SchemaName": "new_Dept_Emp"}] + result = await async_client.tables.list_relationships() + mock_od._list_relationships.assert_called_once_with(filter=None, select=None) + assert result == [{"SchemaName": "new_Dept_Emp"}] + + async def test_list_table_relationships(self, async_client, mock_od): + """list_table_relationships() calls _list_table_relationships and returns its result.""" + mock_od._list_table_relationships.return_value = [{"SchemaName": "new_Dept_Emp"}] + result = await async_client.tables.list_table_relationships("account") + mock_od._list_table_relationships.assert_called_once_with("account", filter=None, select=None) + assert result == [{"SchemaName": "new_Dept_Emp"}] diff --git a/tests/unit/core/test_http_errors.py b/tests/unit/core/test_http_errors.py index 39373e05..a0e3f907 100644 --- a/tests/unit/core/test_http_errors.py +++ b/tests/unit/core/test_http_errors.py @@ -3,8 +3,6 @@ import pytest from azure.core.credentials import TokenCredential -from PowerPlatform.Dataverse.client import DataverseClient -from PowerPlatform.Dataverse.core.config import DataverseConfig from PowerPlatform.Dataverse.core.errors import HttpError from PowerPlatform.Dataverse.core._error_codes import HTTP_404, HTTP_429, HTTP_500 from PowerPlatform.Dataverse.data._odata import _ODataClient diff --git a/tests/unit/models/test_query_builder.py b/tests/unit/models/test_query_builder.py index f40d1e5d..e47cb9a6 100644 --- a/tests/unit/models/test_query_builder.py +++ b/tests/unit/models/test_query_builder.py @@ -891,7 +891,6 @@ def test_to_dataframe_forwards_count_and_annotations(self): def test_to_dataframe_with_record_objects(self): """to_dataframe() handles Record objects (with .data attribute).""" import pandas as pd - from PowerPlatform.Dataverse.models.record import Record mock_query_ops, _ = self._make_od( [ diff --git a/tests/unit/test_phase1_ga.py b/tests/unit/test_phase1_ga.py index ee832029..8069ffd1 100644 --- a/tests/unit/test_phase1_ga.py +++ b/tests/unit/test_phase1_ga.py @@ -391,8 +391,6 @@ def test_dataframe_get_warning_message(self): def test_dataframe_other_methods_no_warning(self): """dataframe.sql(), dataframe.create(), etc. must NOT warn.""" - import pandas as pd - from PowerPlatform.Dataverse.models.record import Record self.client._odata._query_sql.return_value = [] diff --git a/tests/unit/test_phase3_ga.py b/tests/unit/test_phase3_ga.py index f9e53f9f..6802bc0a 100644 --- a/tests/unit/test_phase3_ga.py +++ b/tests/unit/test_phase3_ga.py @@ -18,7 +18,7 @@ import unittest import warnings from dataclasses import dataclass -from unittest.mock import MagicMock, patch +from unittest.mock import MagicMock from azure.core.credentials import TokenCredential From c610af5ce185b731b72f86ecde486e9102ab2992 Mon Sep 17 00:00:00 2001 From: Abel Milash Date: Tue, 19 May 2026 13:11:27 -0700 Subject: [PATCH 03/34] Remove non-async changes from async PR: revert migration tool path, restore typed signatures and docstrings Co-Authored-By: Claude Sonnet 4.6 --- pyproject.toml | 18 ++++++++++-------- src/PowerPlatform/Dataverse/client.py | 2 +- src/PowerPlatform/Dataverse/models/record.py | 4 ++-- .../Dataverse/operations/query.py | 14 ++++++++------ tests/unit/models/test_query_builder.py | 1 + tests/unit/test_phase1_ga.py | 2 ++ tests/unit/test_phase3_ga.py | 2 +- 7 files changed, 25 insertions(+), 18 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 1ddcc51a..e4a11e86 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,7 +40,7 @@ dependencies = [ [project.scripts] dataverse-install-claude-skill = "PowerPlatform.Dataverse._skill_installer:main" -dataverse-migrate = "tools.migrate_v0_to_v1:main" +dataverse-migrate = "PowerPlatform.Dataverse.migration.migrate_v0_to_v1:main" [project.optional-dependencies] async = [ @@ -58,12 +58,12 @@ dev = [ migration = ["libcst>=1.0.0"] [tool.setuptools] -package-dir = {"" = "src", "tools" = "tools"} +package-dir = {"" = "src"} zip-safe = false [tool.setuptools.packages.find] -where = ["src", "."] -include = ["PowerPlatform*", "tools"] +where = ["src"] +include = ["PowerPlatform*"] namespaces = false [tool.setuptools.package-data] @@ -103,10 +103,12 @@ asyncio_mode = "auto" [tool.coverage.run] source = ["src/PowerPlatform"] -omit = [ - "*/Dataverse/_skill_installer.py", - "*/Dataverse/extensions/__init__.py", -] +# Migration codemod is an opt-in tool (requires the [migration] extra). +# It is exercised by tests/unit/test_migration_tool.py and tests/unit/test_phase4_ga.py, +# but its CLI plumbing and libcst error-path branches are not meaningfully testable +# under the default coverage scope. Exclude from coverage reporting so the metric +# reflects core SDK code only. +omit = ["src/PowerPlatform/Dataverse/migration/*"] [tool.coverage.report] fail_under = 90 diff --git a/src/PowerPlatform/Dataverse/client.py b/src/PowerPlatform/Dataverse/client.py index c9a1364f..12ceaaac 100644 --- a/src/PowerPlatform/Dataverse/client.py +++ b/src/PowerPlatform/Dataverse/client.py @@ -105,7 +105,7 @@ def __init__( ) -> None: if config is not None and context is not None: raise ValueError( - "Cannot specify both 'config' and 'context'. " "Pass operation_context via DataverseConfig instead." + "Cannot specify both 'config' and 'context'. Pass operation_context via DataverseConfig instead." ) self.auth = _AuthManager(credential) self._base_url = (base_url or "").rstrip("/") diff --git a/src/PowerPlatform/Dataverse/models/record.py b/src/PowerPlatform/Dataverse/models/record.py index e81f412c..da65e7a5 100644 --- a/src/PowerPlatform/Dataverse/models/record.py +++ b/src/PowerPlatform/Dataverse/models/record.py @@ -6,7 +6,7 @@ from __future__ import annotations from dataclasses import dataclass, field -from typing import Any, Dict, Iterator, KeysView, List, Optional, ValuesView, ItemsView +from typing import Any, Dict, Iterator, KeysView, List, Optional, Union, ValuesView, ItemsView __all__ = ["Record", "QueryResult"] @@ -141,7 +141,7 @@ def __bool__(self) -> bool: def __repr__(self) -> str: return f"QueryResult({len(self.records)} records)" - def __getitem__(self, index): + def __getitem__(self, index: Union[int, slice]) -> Union[Record, "QueryResult"]: result = self.records[index] return QueryResult(result) if isinstance(index, slice) else result diff --git a/src/PowerPlatform/Dataverse/operations/query.py b/src/PowerPlatform/Dataverse/operations/query.py index c927bdf0..869b1197 100644 --- a/src/PowerPlatform/Dataverse/operations/query.py +++ b/src/PowerPlatform/Dataverse/operations/query.py @@ -33,12 +33,14 @@ class QueryOperations: Example:: + from PowerPlatform.Dataverse.models.filters import col + client = DataverseClient(base_url, credential) # Fluent query builder (recommended) for record in (client.query.builder("account") .select("name", "revenue") - .filter_eq("statecode", 0) + .where(col("statecode") == 0) .order_by("revenue", descending=True) .top(100) .execute()): @@ -72,8 +74,8 @@ def builder(self, table: str) -> QueryBuilder: for record in (client.query.builder("account") .select("name", "revenue") - .filter_eq("statecode", 0) - .filter_gt("revenue", 1000000) + .where(col("statecode") == 0) + .where(col("revenue") > 1_000_000) .order_by("revenue", descending=True) .top(100) .page_size(50) @@ -82,11 +84,11 @@ def builder(self, table: str) -> QueryBuilder: With composable expression tree:: - from PowerPlatform.Dataverse.models.filters import eq, gt + from PowerPlatform.Dataverse.models.filters import col for record in (client.query.builder("account") - .where((eq("statecode", 0) | eq("statecode", 1)) - & gt("revenue", 100000)) + .where((col("statecode") == 0) | (col("statecode") == 1)) + .where(col("revenue") > 100_000) .execute()): print(record["name"]) """ diff --git a/tests/unit/models/test_query_builder.py b/tests/unit/models/test_query_builder.py index e47cb9a6..f40d1e5d 100644 --- a/tests/unit/models/test_query_builder.py +++ b/tests/unit/models/test_query_builder.py @@ -891,6 +891,7 @@ def test_to_dataframe_forwards_count_and_annotations(self): def test_to_dataframe_with_record_objects(self): """to_dataframe() handles Record objects (with .data attribute).""" import pandas as pd + from PowerPlatform.Dataverse.models.record import Record mock_query_ops, _ = self._make_od( [ diff --git a/tests/unit/test_phase1_ga.py b/tests/unit/test_phase1_ga.py index 8069ffd1..ee832029 100644 --- a/tests/unit/test_phase1_ga.py +++ b/tests/unit/test_phase1_ga.py @@ -391,6 +391,8 @@ def test_dataframe_get_warning_message(self): def test_dataframe_other_methods_no_warning(self): """dataframe.sql(), dataframe.create(), etc. must NOT warn.""" + import pandas as pd + from PowerPlatform.Dataverse.models.record import Record self.client._odata._query_sql.return_value = [] diff --git a/tests/unit/test_phase3_ga.py b/tests/unit/test_phase3_ga.py index 6802bc0a..f9e53f9f 100644 --- a/tests/unit/test_phase3_ga.py +++ b/tests/unit/test_phase3_ga.py @@ -18,7 +18,7 @@ import unittest import warnings from dataclasses import dataclass -from unittest.mock import MagicMock +from unittest.mock import MagicMock, patch from azure.core.credentials import TokenCredential From 3113d458d4b6cc50d33e1c5ad42b4661104c7edd Mon Sep 17 00:00:00 2001 From: Abel Milash Date: Tue, 19 May 2026 13:25:00 -0700 Subject: [PATCH 04/34] Restore non-async files to match main: libcst in dev, query.py docstring import, test_http_errors imports Co-Authored-By: Claude Sonnet 4.6 --- pyproject.toml | 1 + src/PowerPlatform/Dataverse/operations/query.py | 2 ++ tests/unit/core/test_http_errors.py | 2 ++ 3 files changed, 5 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index e4a11e86..d0d7ba95 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,6 +54,7 @@ dev = [ "isort>=5.12.0", "mypy>=1.0.0", "ruff>=0.1.0", + "libcst>=1.0.0", ] migration = ["libcst>=1.0.0"] diff --git a/src/PowerPlatform/Dataverse/operations/query.py b/src/PowerPlatform/Dataverse/operations/query.py index 869b1197..1f3c8ef2 100644 --- a/src/PowerPlatform/Dataverse/operations/query.py +++ b/src/PowerPlatform/Dataverse/operations/query.py @@ -72,6 +72,8 @@ def builder(self, table: str) -> QueryBuilder: Example: Build and execute a query fluently:: + from PowerPlatform.Dataverse.models.filters import col + for record in (client.query.builder("account") .select("name", "revenue") .where(col("statecode") == 0) diff --git a/tests/unit/core/test_http_errors.py b/tests/unit/core/test_http_errors.py index a0e3f907..39373e05 100644 --- a/tests/unit/core/test_http_errors.py +++ b/tests/unit/core/test_http_errors.py @@ -3,6 +3,8 @@ import pytest from azure.core.credentials import TokenCredential +from PowerPlatform.Dataverse.client import DataverseClient +from PowerPlatform.Dataverse.core.config import DataverseConfig from PowerPlatform.Dataverse.core.errors import HttpError from PowerPlatform.Dataverse.core._error_codes import HTTP_404, HTTP_429, HTTP_500 from PowerPlatform.Dataverse.data._odata import _ODataClient From 9cb9d4a9dbe6cac12c140d7d221505cad9103ceb Mon Sep 17 00:00:00 2001 From: Abel Milash Date: Tue, 19 May 2026 16:54:24 -0700 Subject: [PATCH 05/34] Align async example scripts with sync counterparts - sql_examples.py: expand docstring, add 8-table JOIN, odata_select/expand/bind examples, add sections 31-33 (SQL vs OData, Anti-Patterns, Summary) - functional_testing.py: add async test_sql_encoding(), expand batch test from 7 to 11 steps (two changesets, content-ID chaining, upsert, mixed batch) - installation_example.py: expand docstring with End Users/Developers install sections, Key Differences, Editable Mode Benefits; add [async] extra - prodev_quick_start.py: add cleanup note and 'Why DataFrames?' to docstring - datascience_risk_assessment.py: add Option C (GitHub Copilot SDK) and pip note - alternate_keys_upsert.py: fix Step 6 to use records.list() instead of list_pages() - __init__.py files: add module docstrings matching sync counterparts Co-Authored-By: Claude Sonnet 4.6 --- examples/aio/advanced/__init__.py | 2 + .../aio/advanced/alternate_keys_upsert.py | 14 +- .../advanced/datascience_risk_assessment.py | 6 +- examples/aio/advanced/prodev_quick_start.py | 25 +- examples/aio/advanced/sql_examples.py | 280 +++++++++++++++++- examples/aio/basic/__init__.py | 2 + examples/aio/basic/functional_testing.py | 280 ++++++++++++++++-- examples/aio/basic/installation_example.py | 30 +- 8 files changed, 600 insertions(+), 39 deletions(-) diff --git a/examples/aio/advanced/__init__.py b/examples/aio/advanced/__init__.py index 9a045456..fbb60381 100644 --- a/examples/aio/advanced/__init__.py +++ b/examples/aio/advanced/__init__.py @@ -1,2 +1,4 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT license. + +"""Advanced async examples showcasing complex Dataverse SDK features.""" diff --git a/examples/aio/advanced/alternate_keys_upsert.py b/examples/aio/advanced/alternate_keys_upsert.py index a080975d..97ee221c 100644 --- a/examples/aio/advanced/alternate_keys_upsert.py +++ b/examples/aio/advanced/alternate_keys_upsert.py @@ -230,15 +230,15 @@ async def main(): # Step 6: Verify # ------------------------------------------------------------------ print("\n6. Verifying records...") - async for record in client.records.list_pages( + records = await client.records.list( TABLE_NAME, select=["new_productname", "new_price", KEY_COLUMN.lower()], - ): - for item in record: - ext_id = item.get(KEY_COLUMN.lower(), "?") - name = item.get("new_productname", "?") - price = item.get("new_price", "?") - print(f" {ext_id}: {name} @ ${price}") + ) + for record in records: + ext_id = record.get(KEY_COLUMN.lower(), "?") + name = record.get("new_productname", "?") + price = record.get("new_price", "?") + print(f" {ext_id}: {name} @ ${price}") # ------------------------------------------------------------------ # Step 7: List alternate keys diff --git a/examples/aio/advanced/datascience_risk_assessment.py b/examples/aio/advanced/datascience_risk_assessment.py index 8d88fd68..0800837a 100644 --- a/examples/aio/advanced/datascience_risk_assessment.py +++ b/examples/aio/advanced/datascience_risk_assessment.py @@ -34,10 +34,12 @@ pip install PowerPlatform-Dataverse-Client pip install azure-identity -Additional libraries (optional -- used for visualization and LLM): - pip install matplotlib +Additional libraries (optional -- used for visualization and LLM; not part +of the SDK and must be installed separately. Pick ONE LLM provider): + pip install matplotlib # for charts / visualization pip install azure-ai-inference # Option A: Azure AI Foundry / Azure OpenAI pip install openai # Option B: OpenAI / Azure OpenAI + pip install github-copilot-sdk # Option C: GitHub Copilot SDK (requires Copilot CLI) """ import asyncio diff --git a/examples/aio/advanced/prodev_quick_start.py b/examples/aio/advanced/prodev_quick_start.py index 4cc953a8..9c10852f 100644 --- a/examples/aio/advanced/prodev_quick_start.py +++ b/examples/aio/advanced/prodev_quick_start.py @@ -18,8 +18,31 @@ 5) Query and join data across tables 6) Clean up (delete tables) + Note: The last step (cleanup) automatically deletes all demo tables. + Comment out the cleanup() call in run_demo() if you want to keep the + tables in your environment for inspection. + +Why pandas DataFrames? + This example uses client.dataframe (pandas) instead of raw dict/list CRUD + because DataFrames provide significant advantages for multi-record operations: + + - Batch operations are natural: create 100 records from a DataFrame in one + call vs. looping over 100 dicts + - Column operations (broadcast a value, compute derived fields) are one-liners + instead of for-loops + - Joins and aggregations across tables use pandas merge/groupby -- far more + readable than manual dict matching + - NaN/None handling is built in (clear_nulls flag controls whether missing + values clear server fields or are skipped) + - NumPy type normalization is automatic (int64, float64, Timestamps all + serialize to JSON correctly without manual conversion) + + The SDK also supports plain dict/list CRUD via client.records for single-record + operations or when pandas is not needed. Both approaches use the same underlying + Dataverse Web API calls. + Prerequisites: - pip install PowerPlatform-Dataverse-Client + pip install "PowerPlatform-Dataverse-Client[async]" pip install azure-identity """ diff --git a/examples/aio/advanced/sql_examples.py b/examples/aio/advanced/sql_examples.py index 4edd635c..533dbaee 100644 --- a/examples/aio/advanced/sql_examples.py +++ b/examples/aio/advanced/sql_examples.py @@ -7,12 +7,36 @@ Async equivalent of examples/advanced/sql_examples.py. This example demonstrates everything a SQL developer can do through the -Python SDK's ``client.query.sql()`` and ``client.dataframe.sql()`` methods. - -See examples/advanced/sql_examples.py for the complete capability reference. +Python SDK's ``client.query.sql()`` and ``client.dataframe.sql()`` methods, +based on extensive testing of the Dataverse SQL endpoint (353 test queries). + +Capabilities PROVEN to work: +- SELECT with specific columns +- INNER JOIN, LEFT JOIN (up to 6+ tables) +- COUNT(*), SUM(), AVG(), MIN(), MAX() aggregates +- GROUP BY, DISTINCT, DISTINCT TOP +- WHERE (=, !=, >, <, >=, <=, LIKE, IN, NOT IN, IS NULL, IS NOT NULL, BETWEEN) +- TOP N (0-5000), ORDER BY col [ASC|DESC] +- OFFSET ... FETCH NEXT (server-side pagination) +- Table and column aliases +- Polymorphic lookups (ownerid, customerid) via separate JOINs +- Audit trail (createdby, modifiedby) via systemuser JOINs +- SQL read -> DataFrame transform -> SDK write-back (full round-trip) +- AND/OR, NOT IN, NOT LIKE boolean logic +- Deep JOINs (5-8 tables) with no server depth limit +- SQL helper functions: sql_columns (sql_select/sql_join/sql_joins removed at GA) +- OData helper functions: odata_select, odata_expands, odata_expand, odata_bind +- SQL vs OData side-by-side comparison + +Not supported (server rejects): +- INSERT/UPDATE/DELETE (read-only) -> use client.dataframe.create/update/delete +- Subqueries, CTE, HAVING, UNION +- RIGHT JOIN, FULL OUTER JOIN, CROSS JOIN +- CASE, COALESCE, CAST, string/date/math functions +- Window functions (ROW_NUMBER, RANK) Prerequisites: -- pip install PowerPlatform-Dataverse-Client azure-identity +- pip install "PowerPlatform-Dataverse-Client[async]" azure-identity """ import asyncio @@ -848,6 +872,12 @@ async def _run_examples(client): # 28. Deep JOINs (5-8 tables) # ============================================================== heading(28, "Deep JOINs (5+ Tables) -- No Depth Limit") + print( + "SQL JOINs have no server-imposed depth limit (tested up to 15\n" + "tables). Each JOIN uses indexed foreign key lookups, so\n" + "performance stays consistent. Most real-world queries use\n" + "2-4 tables; deeper JOINs are available when needed." + ) sql = ( "SELECT TOP 3 a.name, c.fullname, o.name as opp, " @@ -865,6 +895,26 @@ async def _run_examples(client): except Exception as e: print(f"[INFO] {e}") + sql = ( + "SELECT TOP 3 a.name, c.fullname, o.name as opp, " + "su.fullname as owner, bu.name as bu, t.name as team, " + "cr.fullname as creator, md.fullname as modifier " + "FROM account a " + "JOIN contact c ON a.accountid = c.parentcustomerid " + "JOIN opportunity o ON a.accountid = o.parentaccountid " + "JOIN systemuser su ON a.ownerid = su.systemuserid " + "JOIN businessunit bu ON su.businessunitid = bu.businessunitid " + "JOIN team t ON bu.businessunitid = t.businessunitid " + "JOIN systemuser cr ON a.createdby = cr.systemuserid " + "JOIN systemuser md ON a.modifiedby = md.systemuserid" + ) + log_call("8-table JOIN") + try: + results = await backoff(lambda: client.query.sql(sql)) + print(f"[OK] 8-table JOIN: {len(results)} rows") + except Exception as e: + print(f"[INFO] {e}") + # ============================================================== # 29. SQL Helper Functions # ============================================================== @@ -885,13 +935,18 @@ async def _run_examples(client): # ============================================================== # 30. OData Helper Functions # ============================================================== - heading(30, "OData Helper Functions (query.odata_expands)") + heading(30, "OData Helper Functions (query.odata_* — deprecated at GA)") print( - "odata_expands() is available without deprecation.\n" - "odata_select(), odata_expand(), and odata_bind() are deprecated\n" - "at GA -- use the typed query builder instead." + "odata_select(), odata_expand(), and odata_bind() still work at GA\n" + "but emit DeprecationWarning. Use the typed query builder instead.\n" + "odata_expands() is kept without deprecation." ) + # odata_select + log_call(f"client.query.odata_select('{parent_table}')") + odata_cols = await client.query.odata_select(parent_table) + print(f"[OK] {len(odata_cols)} columns for $select: {odata_cols[:5]}...") + # odata_expands log_call(f"client.query.odata_expands('{child_table}')") try: @@ -902,8 +957,215 @@ async def _run_examples(client): except Exception as e: print(f"[WARN] {e}") + # odata_expand (single target) + try: + nav = await client.query.odata_expand(child_table, parent_table) + print(f"\n[OK] odata_expand('{child_table}', '{parent_table}') = '{nav}'") + print(" Usage: client.query.builder('" + child_table + "').expand('" + nav + "').execute()") + except Exception as e: + print(f"[WARN] {e}") + + # odata_bind + log_call("client.query.odata_bind(...)") + try: + bind = await client.query.odata_bind(child_table, parent_table, team_ids[0]) + print(f"[OK] {bind}") + print(" Merge into create/update payload: {{'new_Title': 'X', **bind}}") + except Exception as e: + print(f"[WARN] {e}") + + # ============================================================== + # 31. SQL vs OData Comparison + # ============================================================== + heading(31, "SQL vs OData -- Side-by-Side Comparison") + print("Both SQL and OData can query Dataverse. Here's how they compare.") + + print(""" ++-------------------------------+------------------------+------------------------+ +| Capability | SQL (client.query.sql) | OData (records.get) | ++-------------------------------+------------------------+------------------------+ +| Read data | YES | YES | +| Write data | NO (read-only) | YES (create/update/del)| +| JOIN depth | No limit (tested 15) | $expand 10-level max | +| JOIN types | INNER, LEFT | $expand (single-valued)| +| Aggregates (COUNT, SUM, etc.) | YES (server-side) | Limited ($apply) | +| GROUP BY | YES (server-side) | Via $apply (complex) | +| DISTINCT | YES | Not directly | +| Pagination | OFFSET FETCH | @odata.nextLink | +| Max results | 5000 per query | 5000 per page | +| Column discovery | sql_columns | odata_expands (kept) | +| JOIN discovery | write manually/fetchxml | odata_expand (deprecated)| +| Lookup binding | N/A (read-only) | odata_bind | +| SELECT * | YES (SDK auto-expands) | Not applicable | +| Polymorphic lookups | Separate JOINs | $expand by nav prop | +| Return format | list[Record] / DF | pages of Record / DF | +| Subqueries | NO (chain SQL calls) | NO ($filter only) | +| Functions (CASE, CAST, etc.) | NO | NO | ++-------------------------------+------------------------+------------------------+ + +When to use SQL: + - Complex JOINs across 3+ tables + - Aggregates and GROUP BY + - DISTINCT queries + - Familiar SQL syntax preferred + - Read-only analysis / reporting + +When to use OData (records.get): + - Need to write data (create/update/delete) + - Simple single-table or 1-level expand queries + - Need automatic paging (nextLink) + - Prefer typed QueryBuilder API +""") + + # Live comparison: same query via SQL and OData + print("-- Live comparison: account + contact --") + import time as _time + + # SQL version + t0 = _time.time() + try: + sql_rows = await backoff( + lambda: client.query.sql( + "SELECT TOP 5 a.name, c.fullname " + "FROM account a " + "JOIN contact c ON a.accountid = c.parentcustomerid" + ) + ) + sql_time = _time.time() - t0 + print(f" SQL JOIN: {len(sql_rows)} rows in {sql_time:.2f}s") + except Exception as e: + sql_time = _time.time() - t0 + print(f" SQL JOIN: error ({sql_time:.2f}s): {e}") + + # OData version (expand) + t0 = _time.time() + try: + odata_rows = list( + await backoff( + lambda: client.records.list( + "account", + select=["name"], + top=5, + ) + ) + ) + odata_time = _time.time() - t0 + print(f" OData records.list: {len(odata_rows)} rows in {odata_time:.2f}s") + except Exception as e: + odata_time = _time.time() - t0 + print(f" OData records.list: error ({odata_time:.2f}s): {e}") + + # ============================================================== + # 32. Anti-Patterns & Best Practices + # ============================================================== + heading(32, "IMPORTANT: Anti-Patterns & Best Practices") + print(""" +=== ANTI-PATTERNS (avoid these -- they hurt shared database performance) === + +1. CARTESIAN PRODUCTS (FROM table1, table2 without ON) + BAD: SELECT a.name, c.fullname FROM account a, contact c + WHY: Produces rows_a * rows_b intermediate rows. With 5000-row tables, + that's 25 MILLION rows the server must process before capping at 5000. + FIX: Always use explicit JOIN with ON clause. + +2. LEADING-WILDCARD LIKE (LIKE '%value') + BAD: SELECT name FROM account WHERE name LIKE '%corp' + WHY: Forces a FULL TABLE SCAN -- cannot use indexes. On tables with + millions of rows, this monopolizes shared database resources and + slows down OTHER users' queries on the same database. + FIX: Use trailing wildcards: LIKE 'corp%' (uses indexes efficiently). + If you must search mid-string, add TOP to limit scan scope. + +3. NO FILTER ON LARGE SYSTEM TABLES + BAD: SELECT name FROM role + WHY: System tables (role, asyncoperation, sdkmessageprocessingstep) + can have 5000+ rows. Unfiltered queries return max rows. + FIX: Always add WHERE filters and TOP when querying system tables. + +4. SELECT * (BLOCKED -- ValidationError) + BAD: SELECT * FROM account + WHY: SELECT * is intentionally rejected -- not a technical limitation. + Wide entities (account has 307 columns) make wildcard selects + extremely expensive on shared database infrastructure. + FIX: List only the columns you need: SELECT name, revenue FROM account + Or discover columns first: + cols = await client.query.sql_columns("account") + For JOINs, always qualify columns from each table: + SELECT a.name, c.fullname FROM account a JOIN contact c ON ... + +5. DEEP JOINS WITHOUT TOP + OK: SELECT TOP 100 a.name, ... FROM account a JOIN ... (15 tables) + BAD: SELECT a.name, ... FROM account a JOIN ... (15 tables, no TOP) + WHY: Deep JOINs are safe with proper FK relationships and TOP. + Without TOP, the server processes up to 5000 rows across all joins. + FIX: Always include TOP N for multi-table JOINs. + +SDK guardrails: + - Patterns #1 (writes), unsupported syntax (CROSS/RIGHT/FULL JOIN, + UNION, HAVING, CTE, subqueries), and #4 (SELECT *) + -> ValidationError (blocked). + - Pattern #2 (cartesian FROM a, b) -> UserWarning (advisory). + - Server enforces 5000-row cap on all queries (#3, #5). + - Use sql_columns() to discover valid column names. + - Write JOIN clauses manually or use fetchxml() for complex queries. +""") + + # ============================================================== + # 33. Summary + # ============================================================== + heading(33, "Summary -- SQL Capabilities Reference") + print(""" ++-------------------------------+----------+----------------------------------------+ +| Feature | SQL | Notes / SDK Fallback | ++-------------------------------+----------+----------------------------------------+ +| SELECT col1, col2 | YES | Use LogicalName (lowercase) | +| SELECT * | NO | Specify columns explicitly | +| WHERE =, !=, >, <, LIKE, IN | YES | | +| AND, OR, parentheses | YES | Full boolean logic | +| NOT IN, NOT LIKE | YES | | +| IS NULL, IS NOT NULL, BETWEEN | YES | | +| TOP N (0-5000) | YES | Max 5000 per query | +| ORDER BY col [ASC|DESC] | YES | Multiple columns supported | +| OFFSET n FETCH NEXT m | YES | Server-side pagination | +| Table/Column aliases | YES | | +| DISTINCT / DISTINCT TOP | YES | Works with JOINs too | +| COUNT, SUM, AVG, MIN, MAX | YES | All 5 standard aggregates | +| GROUP BY | YES | Server-side grouping | +| INNER JOIN | YES | 15+ tables tested (no depth limit) | +| LEFT JOIN | YES | | +| Self JOIN | YES | Same table with different aliases | +| SQL -> DataFrame | YES | client.dataframe.sql(query) | +| Polymorphic lookups | YES | Separate JOINs per target type | +| Nested polymorphic chains | YES | e.g. opp -> acct -> contact -> owner | +| Audit trail (createdby, etc.) | YES | JOIN to systemuser | +| SQL read -> DF write-back | YES | dataframe.sql() + .update()/.create() | +| SQL column discovery | YES | query.sql_columns() | +| SQL JOIN clause | manual | write directly or use fetchxml() | +| OData column discovery | YES | query.odata_select() | +| OData expand discovery | YES | query.odata_expands() / odata_expand() | +| OData bind builder | YES | query.odata_bind() | ++-------------------------------+----------+----------------------------------------+ +| HAVING | NO | Filter before GROUP BY | +| Subqueries / CTE | NO | Chain multiple SQL calls | +| RIGHT/FULL OUTER/CROSS JOIN | NO | Rewrite as LEFT/INNER JOIN | +| UNION / UNION ALL | NO | Separate queries + pd.concat() | +| CASE, COALESCE, CAST | NO | Post-process in Python/pandas | +| String/Date/Math functions | NO | Post-process in Python/pandas | +| Window fns (ROW_NUMBER, RANK) | NO | Post-process in Python/pandas | +| INSERT / UPDATE / DELETE | NO | dataframe.create/update/delete() | ++-------------------------------+----------+----------------------------------------+ + +SQL-First Workflow (no OData knowledge needed): + 1. Discover schema: cols = await client.query.sql_columns("account") + 2. Write JOIN: j = "JOIN account a ON c.parentcustomerid = a.accountid" + 3. Query with SQL: df = await client.dataframe.sql(f"SELECT c.fullname, a.name FROM contact c {j}") + 4. Transform: df["col"] = df["col"] * 1.1 + 5. Write back: await client.dataframe.update("account", df, id_column="accountid") + 6. Verify: df2 = await client.dataframe.sql("SELECT ...") +""") + finally: - heading(31, "Cleanup") + heading(34, "Cleanup") for tbl in [child_table, parent_table]: log_call(f"client.tables.delete('{tbl}')") try: diff --git a/examples/aio/basic/__init__.py b/examples/aio/basic/__init__.py index 9a045456..bbcc2df4 100644 --- a/examples/aio/basic/__init__.py +++ b/examples/aio/basic/__init__.py @@ -1,2 +1,4 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT license. + +"""Basic async examples for getting started with the Dataverse SDK.""" diff --git a/examples/aio/basic/functional_testing.py b/examples/aio/basic/functional_testing.py index b194050e..a9b1f71d 100644 --- a/examples/aio/basic/functional_testing.py +++ b/examples/aio/basic/functional_testing.py @@ -421,9 +421,145 @@ async def test_query_records(client: AsyncDataverseClient, table_info: Dict[str, print(" This might be expected if the table is very new.") +async def test_sql_encoding( + client: AsyncDataverseClient, + table_info: Dict[str, Any], + retrieved_record: Dict[str, Any], +) -> None: + """Verify SQL encoding parity between client.query.sql() and batch.query.sql(). + + The direct path (client.query.sql) delegates to _build_sql which encodes the + SQL via urllib.parse.quote(safe=''), producing %20 for spaces. The batch path + uses the same _build_sql method, so both should behave identically. + + Specifically tests SQL containing: + - Spaces in a WHERE string literal (requires %20 encoding) + - Colons in a WHERE string literal (the HH:MM:SS timestamp in the name) + + Both paths are run against the same SQL and their results are compared + to confirm the encoding produces matching Dataverse responses. + """ + print("\n-> SQL Encoding Verification Test") + print("=" * 50) + + table_schema_name = table_info.get("table_schema_name") + logical_name = table_info.get("table_logical_name", table_schema_name.lower()) + attr_prefix = table_schema_name.split("_", 1)[0] if "_" in table_schema_name else table_schema_name + name_col = f"{attr_prefix}_name" + known_name = retrieved_record.get(name_col, "") + + try: + # ------------------------------------------------------------------ + # Case 1: Basic SELECT — no special characters in WHERE clause. + # ------------------------------------------------------------------ + basic_sql = f"SELECT TOP 5 {name_col} FROM {logical_name}" + print(f" [1/3] Basic SELECT (no special chars): {basic_sql}") + + direct_rows = await client.query.sql(basic_sql) + direct_count = len(direct_rows) + + batch = client.batch.new() + batch.query.sql(basic_sql) + result = await batch.execute() + batch_count = ( + len(result.responses[0].data.get("value", [])) + if result.responses and result.responses[0].is_success and result.responses[0].data + else 0 + ) + + assert direct_count == batch_count, f"Row count mismatch: client={direct_count}, batch={batch_count}" + print(f" [OK] Both paths returned {direct_count} rows") + + # ------------------------------------------------------------------ + # Case 2: WHERE clause with spaces and colons in the string literal. + # ------------------------------------------------------------------ + if known_name: + escaped_name = known_name.replace("'", "''") + where_sql = f"SELECT TOP 1 {name_col} FROM {logical_name} WHERE {name_col} = '{escaped_name}'" + print(f" [2/3] WHERE with spaces/colons: ...WHERE {name_col} = '{escaped_name}'") + + direct_rows_where = await client.query.sql(where_sql) + direct_where_count = len(direct_rows_where) + + batch2 = client.batch.new() + batch2.query.sql(where_sql) + result2 = await batch2.execute() + batch_where_count = ( + len(result2.responses[0].data.get("value", [])) + if result2.responses and result2.responses[0].is_success and result2.responses[0].data + else 0 + ) + + assert ( + direct_where_count == batch_where_count + ), f"Row count mismatch on WHERE query: client={direct_where_count}, batch={batch_where_count}" + assert direct_where_count == 1, f"Expected exactly 1 row for known record name, got {direct_where_count}" + direct_name = direct_rows_where[0].get(name_col) + assert direct_name == known_name, f"Returned name '{direct_name}' does not match expected '{known_name}'" + print(f" [OK] Both paths found the record: '{direct_name}'") + else: + print(" [2/3] Skipped WHERE test — record name not available in retrieved_record") + + # ------------------------------------------------------------------ + # Case 3: WHERE clause with an equals sign inside the string literal. + # ------------------------------------------------------------------ + print(" [3/3] WHERE with '=' in string literal (tests %3D encoding)") + equals_name = f"SQL=Test {datetime.now().strftime('%H:%M:%S')}" + eq_id = await client.records.create(table_schema_name, {name_col: equals_name}) + try: + escaped_eq = equals_name.replace("'", "''") + eq_sql = f"SELECT TOP 1 {name_col} FROM {logical_name} WHERE {name_col} = '{escaped_eq}'" + + direct_eq_rows = await client.query.sql(eq_sql) + direct_eq_count = len(direct_eq_rows) + + batch3 = client.batch.new() + batch3.query.sql(eq_sql) + result3 = await batch3.execute() + batch_eq_count = ( + len(result3.responses[0].data.get("value", [])) + if result3.responses and result3.responses[0].is_success and result3.responses[0].data + else 0 + ) + + assert ( + direct_eq_count == batch_eq_count + ), f"Row count mismatch on '=' query: client={direct_eq_count}, batch={batch_eq_count}" + assert direct_eq_count == 1, f"Expected 1 row for '=' record, got {direct_eq_count}" + print(f" [OK] Both paths found record with '=' in name: '{direct_eq_rows[0].get(name_col)}'") + finally: + await client.records.delete(table_schema_name, eq_id) + + print("[OK] SQL encoding verification passed — %20/%3D encoding is consistent across both paths") + + except AssertionError as e: + print(f"[ERR] Encoding parity assertion failed: {e}") + raise + except Exception as e: + print(f"[WARN] SQL encoding test encountered an issue: {e}") + print(" Check that the test table exists and has at least one record.") + + async def test_batch_all_operations(client: AsyncDataverseClient, table_info: Dict[str, Any]) -> None: - """Test batch operations using the async batch client.""" - print("\n-> Batch Operations Test") + """Test every available batch operation type in a structured sequence. + + Operations covered: + records.create (single + CreateMultiple) + records.retrieve (single by ID) + records.update (single PATCH + UpdateMultiple) + records.delete (multi, use_bulk_delete=False) + records.upsert (graceful — requires configured alternate key) + tables.add_columns + tables.remove_columns + query.sql + changeset happy path (create + update via content-ID ref + delete) + changeset rollback (failing op rolls back entire changeset) + two changesets in one batch (Content-IDs are globally unique across + the batch via a shared counter) + content-ID reference chaining ($n refs) across multiple creates in one + changeset — regression guard for the shared counter fix + execute(continue_on_error=True) — mixed success/failure + """ + print("\n-> Batch Operations Test (All Operations)") print("=" * 50) table_schema_name = table_info.get("table_schema_name") @@ -432,8 +568,8 @@ async def test_batch_all_operations(client: AsyncDataverseClient, table_info: Di all_ids: list = [] try: - # [1] CREATE — single + CreateMultiple - print("\n[1/7] Create — single + CreateMultiple") + # [1/11] CREATE — single + CreateMultiple + print("\n[1/11] Create — single + CreateMultiple (2 ops, 1 POST $batch)") batch = client.batch.new() batch.records.create( table_schema_name, @@ -466,10 +602,10 @@ async def test_batch_all_operations(client: AsyncDataverseClient, table_info: Di else: print(f"[OK] {len(result.succeeded)} ops → {len(all_ids)} records created") - # [2] READ — retrieve + list + query.sql + # [2/11] READ — retrieve + list + query.sql if all_ids: annotation = "OData.Community.Display.V1.FormattedValue" - print(f"\n[2/7] Read — records.retrieve + records.list + query.sql") + print(f"\n[2/11] Read — records.retrieve + records.list + query.sql") batch = client.batch.new() batch.records.retrieve( table_schema_name, @@ -489,18 +625,18 @@ async def test_batch_all_operations(client: AsyncDataverseClient, table_info: Di result = await batch.execute() print(f"[OK] {len(result.succeeded)} succeeded, {len(result.failed)} failed") - # [3] UPDATE — single + multiple + # [3/11] UPDATE — single + multiple if len(all_ids) >= 2: - print(f"\n[3/7] Update — single PATCH + UpdateMultiple") + print(f"\n[3/11] Update — single PATCH + UpdateMultiple") batch = client.batch.new() batch.records.update(table_schema_name, all_ids[0], {f"{attr_prefix}_count": 10}) batch.records.update(table_schema_name, all_ids[1:], {f"{attr_prefix}_count": 20}) result = await batch.execute() print(f"[OK] {len(result.succeeded)} updates succeeded") - # [4] CHANGESET (happy path) — create + update via content-ID + delete + # [4/11] CHANGESET (happy path) — create + update via content-ID + delete if all_ids: - print("\n[4/7] Changeset (happy path) — create + update(ref) + delete") + print("\n[4/11] Changeset (happy path) — create + update(ref) + delete") batch = client.batch.new() async with batch.changeset() as cs: ref = cs.records.create( @@ -523,8 +659,8 @@ async def test_batch_all_operations(client: AsyncDataverseClient, table_info: Di all_ids[-1] = new_id print(f"[OK] {len(result.succeeded)} ops committed atomically") - # [5] CHANGESET (rollback) - print("\n[5/7] Changeset (rollback) — failing update rolls back create") + # [5/11] CHANGESET (rollback) + print("\n[5/11] Changeset (rollback) — failing update rolls back create") batch = client.batch.new() async with batch.changeset() as cs: cs.records.create( @@ -543,10 +679,76 @@ async def test_batch_all_operations(client: AsyncDataverseClient, table_info: Di print("[WARN] Expected rollback but changeset succeeded (unexpected)") all_ids.extend(result.entity_ids) - # [6] ADD/REMOVE COLUMNS + # [6/11] TWO CHANGESETS — Content-IDs are unique across the entire batch + print("\n[6/11] Two changesets in one batch — globally unique Content-IDs across changesets") + batch = client.batch.new() + async with batch.changeset() as cs1: + ref1 = cs1.records.create( + table_schema_name, + { + f"{attr_prefix}_name": f"CS1-E {datetime.now().strftime('%H:%M:%S')}", + f"{attr_prefix}_count": 10, + f"{attr_prefix}_is_active": False, + }, + ) + cs1.records.update(table_schema_name, ref1, {f"{attr_prefix}_is_active": True}) + async with batch.changeset() as cs2: + ref2 = cs2.records.create( + table_schema_name, + { + f"{attr_prefix}_name": f"CS2-F {datetime.now().strftime('%H:%M:%S')}", + f"{attr_prefix}_count": 20, + f"{attr_prefix}_is_active": False, + }, + ) + cs2.records.update(table_schema_name, ref2, {f"{attr_prefix}_is_active": True}) + result = await batch.execute() + if result.has_errors: + for item in result.failed: + print(f"[WARN] Two-changeset error {item.status_code}: {item.error_message}") + else: + cs_ids = list(result.entity_ids) + all_ids.extend(cs_ids) + print( + f"[OK] Both changesets committed — {len(cs_ids)} records created " + f"with globally unique Content-IDs across changesets: {cs_ids}" + ) + + # [7/11] CONTENT-ID REFERENCE CHAINING — two creates + two updates in one changeset + print("\n[7/11] Content-ID reference chaining — two creates + two updates via $n refs") + batch = client.batch.new() + async with batch.changeset() as cs: + ref_a = cs.records.create( + table_schema_name, + { + f"{attr_prefix}_name": f"Chain-A {datetime.now().strftime('%H:%M:%S')}", + f"{attr_prefix}_count": 0, + f"{attr_prefix}_is_active": False, + }, + ) + ref_b = cs.records.create( + table_schema_name, + { + f"{attr_prefix}_name": f"Chain-B {datetime.now().strftime('%H:%M:%S')}", + f"{attr_prefix}_count": 0, + f"{attr_prefix}_is_active": False, + }, + ) + cs.records.update(table_schema_name, ref_a, {f"{attr_prefix}_count": 100}) + cs.records.update(table_schema_name, ref_b, {f"{attr_prefix}_count": 200}) + result = await batch.execute() + if result.has_errors: + for item in result.failed: + print(f"[WARN] Chaining error {item.status_code}: {item.error_message}") + else: + chain_ids = list(result.entity_ids) + all_ids.extend(chain_ids) + print(f"[OK] Both records created and updated via content-ID refs {ref_a} and {ref_b}: {chain_ids}") + + # [8/11] ADD/REMOVE COLUMNS col_a = f"{attr_prefix}_batch_extra_a" col_b = f"{attr_prefix}_batch_extra_b" - print(f"\n[6/7] Batch tables.add_columns + tables.remove_columns") + print(f"\n[8/11] Batch tables.add_columns + tables.remove_columns") batch = client.batch.new() batch.tables.add_columns(table_schema_name, {col_a: "string"}) batch.tables.add_columns(table_schema_name, {col_b: "int"}) @@ -561,9 +763,51 @@ async def test_batch_all_operations(client: AsyncDataverseClient, table_info: Di for item in result.failed: print(f"[WARN] add_columns error {item.status_code}: {item.error_message}") - # [7] DELETE + # [9/11] UPSERT — graceful (no alternate key configured on test table) + print(f"\n[9/11] Upsert — UpsertItem with alternate key (expected to fail: no alt key on test table)") + try: + batch = client.batch.new() + batch.records.upsert( + table_schema_name, + [ + UpsertItem( + alternate_key={f"{attr_prefix}_name": f"Upsert-E {datetime.now().strftime('%H:%M:%S')}"}, + record={f"{attr_prefix}_count": 5, f"{attr_prefix}_is_active": True}, + ) + ], + ) + result = await batch.execute() + if result.has_errors: + print(f"[WARN] Upsert failed as expected (no alternate key configured): {result.failed[0].status_code}") + else: + upsert_ids = list(result.entity_ids) + all_ids.extend(upsert_ids) + print(f"[OK] Upsert succeeded: {len(upsert_ids)} record(s) — alternate key was accepted") + except Exception as e: + print(f"[WARN] Upsert skipped due to exception: {e}") + + # [10/11] MIXED BATCH with continue_on_error + if all_ids: + print(f"\n[10/11] Mixed batch (continue_on_error=True) — 1 bad get + 1 good get") + batch = client.batch.new() + batch.records.get( + table_schema_name, + "00000000-0000-0000-0000-000000000002", + select=[f"{attr_prefix}_name"], + ) + batch.records.get( + table_schema_name, + all_ids[0], + select=[f"{attr_prefix}_name"], + ) + result = await batch.execute(continue_on_error=True) + print(f"[OK] Succeeded: {len(result.succeeded)}, Failed: {len(result.failed)}") + for item in result.failed: + print(f" Expected failure: {item.status_code} {item.error_message}") + + # [11/11] DELETE if all_ids: - print(f"\n[7/7] Delete — {len(all_ids)} records (use_bulk_delete=False)") + print(f"\n[11/11] Delete — {len(all_ids)} records (use_bulk_delete=False)") batch = client.batch.new() batch.records.delete(table_schema_name, all_ids, use_bulk_delete=False) result = await batch.execute(continue_on_error=True) @@ -866,9 +1110,10 @@ async def main(): async with client: table_info = await ensure_test_table(client) record_id = await test_create_record(client, table_info) - await test_read_record(client, table_info, record_id) + retrieved_record = await test_read_record(client, table_info, record_id) await test_query_records(client, table_info) await test_relationships(client) + await test_sql_encoding(client, table_info, retrieved_record) await test_batch_all_operations(client, table_info) print("\nAsync Functional Test Summary") @@ -879,6 +1124,7 @@ async def main(): print("[OK] Record Reading: Success") print("[OK] Record Querying (list, list_pages, builder, fetchxml): Success") print("[OK] Relationship Operations: Success") + print("[OK] SQL Encoding: Success") print("[OK] Batch Operations: Success") print("\nYour async PowerPlatform Dataverse Client SDK is fully functional!") diff --git a/examples/aio/basic/installation_example.py b/examples/aio/basic/installation_example.py index df958c17..46c97aa1 100644 --- a/examples/aio/basic/installation_example.py +++ b/examples/aio/basic/installation_example.py @@ -11,9 +11,33 @@ ## Installation -```bash -pip install PowerPlatform-Dataverse-Client azure-identity -``` +### For End Users (Production/Consumption): +1. Install the published SDK from PyPI with the async extra: + ```bash + pip install "PowerPlatform-Dataverse-Client[async]" + ``` + +2. Install Azure Identity for authentication: + ```bash + pip install azure-identity + ``` + +### For Developers (Contributing/Local Development): +1. Clone the repository and navigate to the project directory +2. Install in editable/development mode: + ```bash + pip install -e ".[async,dev]" + ``` + +**Key Differences:** +- `pip install "PowerPlatform-Dataverse-Client[async]"` → Downloads and installs the published package from PyPI with aiohttp +- `pip install -e ".[async,dev]"` → Installs from local source code in "editable" mode + +**Editable Mode Benefits:** +- Changes to source code are immediately available (no reinstall needed) +- Perfect for development, testing, and contributing +- Examples and tests can access the local codebase +- Supports debugging and live code modifications ## What This Script Does From c4d5614f668f29eba8eeb53ed6f009b99ece47e6 Mon Sep 17 00:00:00 2001 From: Abel Milash Date: Tue, 19 May 2026 17:19:22 -0700 Subject: [PATCH 06/34] Add async client documentation to README and SDK use skill Documents AsyncDataverseClient installation, quick start, query builder, batch/changesets, and DataFrame patterns in README.md and both copies of the dataverse-sdk-use SKILL.md. Co-Authored-By: Claude Sonnet 4.6 --- .claude/skills/dataverse-sdk-use/SKILL.md | 109 ++++++++++++++++++ README.md | 107 ++++++++++++++++- .../claude_skill/dataverse-sdk-use/SKILL.md | 109 ++++++++++++++++++ 3 files changed, 320 insertions(+), 5 deletions(-) diff --git a/.claude/skills/dataverse-sdk-use/SKILL.md b/.claude/skills/dataverse-sdk-use/SKILL.md index d25815d7..a3c5ebc1 100644 --- a/.claude/skills/dataverse-sdk-use/SKILL.md +++ b/.claude/skills/dataverse-sdk-use/SKILL.md @@ -588,6 +588,115 @@ except ValidationError as e: 10. **Test in non-production environments** first 11. **Use named constants** - Import cascade behavior constants from `PowerPlatform.Dataverse.common.constants` +## Async Client + +The SDK ships a full async client, `AsyncDataverseClient`, under `PowerPlatform.Dataverse.aio`. Requires the `[async]` extra: `pip install "PowerPlatform-Dataverse-Client[async]"`. + +### Import +```python +from azure.identity.aio import InteractiveBrowserCredential # or ClientSecretCredential, etc. +from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient +``` + +### Client Initialization +```python +# Context manager (recommended -- closes session and clears caches automatically) +async with AsyncDataverseClient("https://yourorg.crm.dynamics.com", credential) as client: + ... # all operations here + +# Standalone (call aclose() in a finally block) +client = AsyncDataverseClient("https://yourorg.crm.dynamics.com", credential) +try: + ... +finally: + await client.aclose() +``` + +### CRUD Operations +Every sync method has an async equivalent -- add `await`: +```python +# Create +account_id = await client.records.create("account", {"name": "Contoso Ltd"}) + +# Read +account = await client.records.retrieve("account", account_id, select=["name", "telephone1"]) + +# Update +await client.records.update("account", account_id, {"telephone1": "555-0200"}) + +# Delete +await client.records.delete("account", account_id) + +# Bulk create +ids = await client.records.create("account", [{"name": "A"}, {"name": "B"}]) +``` + +### Query Builder +```python +from PowerPlatform.Dataverse.models.filters import col + +# Collect all results +result = await ( + client.query.builder("account") + .select("name", "telephone1") + .where(col("statecode") == 0) + .top(10) + .execute() +) +for record in result: + print(record["name"]) + +# Lazy page iteration (memory-efficient) +async for page in ( + client.query.builder("account") + .select("name") + .page_size(500) + .execute_pages() +): + for record in page: + print(record["name"]) + +# SQL query +rows = await client.query.sql("SELECT TOP 5 name FROM account") + +# FetchXML +xml = '' +rows = await client.query.fetchxml(xml).execute() +``` + +### Batch and Changesets +```python +# Plain batch +batch = client.batch.new() +batch.records.create("account", {"name": "Alpha"}) +result = await batch.execute() + +# Atomic changeset +batch = client.batch.new() +async with batch.changeset() as cs: + ref = cs.records.create("contact", {"firstname": "Alice"}) + cs.records.update("account", account_id, {"primarycontactid@odata.bind": ref}) +result = await batch.execute() +``` + +### DataFrame Operations +```python +import pandas as pd + +# Query to DataFrame +result = await ( + client.query.builder("account") + .select("name", "telephone1") + .where(col("statecode") == 0) + .execute() +) +df = result.to_dataframe() + +# Create from DataFrame +new_accounts = pd.DataFrame([{"name": "Contoso"}, {"name": "Fabrikam"}]) +ids = await client.dataframe.create("account", new_accounts) +``` + ## Additional Resources Load these resources as needed during development: diff --git a/README.md b/README.md index 21abb591..777884ce 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ A Python client library for Microsoft Dataverse that provides a unified interfac - [Key features](#key-features) - [Getting started](#getting-started) - [Prerequisites](#prerequisites) - - [Install the package](#install-the-package) + - [Install the package](#install-the-package) - [Authenticate the client](#authenticate-the-client) - [Key concepts](#key-concepts) - [Examples](#examples) @@ -30,6 +30,7 @@ A Python client library for Microsoft Dataverse that provides a unified interfac - [Relationship management](#relationship-management) - [File operations](#file-operations) - [Batch operations](#batch-operations) +- [Async client](#async-client) - [Next steps](#next-steps) - [Troubleshooting](#troubleshooting) - [Contributing](#contributing) @@ -53,7 +54,7 @@ A Python client library for Microsoft Dataverse that provides a unified interfac ### Prerequisites -- **Python 3.10+** (3.10, 3.11, 3.12, 3.13 supported) +- **Python 3.10+** (3.10, 3.11, 3.12, 3.13 supported) - **Microsoft Dataverse environment** with appropriate permissions - **OAuth authentication configured** for your application @@ -92,7 +93,7 @@ The client requires any Azure Identity `TokenCredential` implementation for OAut ```python from azure.identity import ( - InteractiveBrowserCredential, + InteractiveBrowserCredential, ClientSecretCredential, CertificateCredential, AzureCliCredential @@ -103,7 +104,7 @@ from PowerPlatform.Dataverse.client import DataverseClient credential = InteractiveBrowserCredential() # Browser authentication # credential = AzureCliCredential() # If logged in via 'az login' -# Production options +# Production options # credential = ClientSecretCredential(tenant_id, client_id, client_secret) # credential = CertificateCredential(tenant_id, client_id, cert_path) @@ -783,6 +784,102 @@ result = batch.execute() For a complete example see [examples/advanced/batch.py](https://github.com/microsoft/PowerPlatform-DataverseClient-Python/blob/main/examples/advanced/batch.py). +## Async client + +The SDK ships a full async client, `AsyncDataverseClient`, for use in async applications. It mirrors every operation of the sync client — the same namespaces (`records`, `query`, `tables`, `files`, `batch`), the same method signatures, and the same return types. + +### Install + +The async client requires `aiohttp`, which is an optional extra: + +```bash +pip install "PowerPlatform-Dataverse-Client[async]" +``` + +### Quick start + +```python +import asyncio +from azure.identity.aio import InteractiveBrowserCredential +from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient + +async def main(): + credential = InteractiveBrowserCredential() + try: + async with AsyncDataverseClient("https://yourorg.crm.dynamics.com", credential) as client: + # Create a contact + contact_id = await client.records.create("contact", {"firstname": "John", "lastname": "Doe"}) + + # Read it back + contact = await client.records.retrieve("contact", contact_id, select=["firstname", "lastname"]) + print(f"Created: {contact['firstname']} {contact['lastname']}") + + # Clean up + await client.records.delete("contact", contact_id) + finally: + await credential.close() + +asyncio.run(main()) +``` + +### Standalone usage (without `async with`) + +```python +client = AsyncDataverseClient("https://yourorg.crm.dynamics.com", credential) +try: + account_id = await client.records.create("account", {"name": "Contoso Ltd"}) +finally: + await client.aclose() +``` + +### Query builder + +The async query builder API is identical to the sync one: + +```python +from PowerPlatform.Dataverse.models.filters import col + +# Execute and collect all results +result = await ( + client.query.builder("account") + .select("name", "telephone1") + .where(col("statecode") == 0) + .top(10) + .execute() +) +for record in result: + print(record["name"]) + +# Lazy page-by-page iteration (memory-efficient for large sets) +async for page in ( + client.query.builder("account") + .select("name") + .page_size(500) + .execute_pages() +): + for record in page: + print(record["name"]) +``` + +### Batch and changesets + +```python +batch = client.batch.new() +batch.records.create("account", {"name": "Alpha"}) +batch.records.create("account", {"name": "Beta"}) +result = await batch.execute() +print(f"Created {len(list(result.entity_ids))} records") + +# Atomic changeset +batch = client.batch.new() +async with batch.changeset() as cs: + ref = cs.records.create("contact", {"firstname": "Alice"}) + cs.records.update("account", account_id, {"primarycontactid@odata.bind": ref}) +result = await batch.execute() +``` + +See [examples/aio/](https://github.com/microsoft/PowerPlatform-DataverseClient-Python/tree/main/examples/aio) for async equivalents of all sync examples. + ## Next steps ### More sample code @@ -808,7 +905,7 @@ For comprehensive information on Microsoft Dataverse and related technologies: | Resource | Description | |----------|-------------| | **[Dataverse Developer Guide](https://learn.microsoft.com/power-apps/developer/data-platform/)** | Complete developer documentation for Microsoft Dataverse | -| **[Dataverse Web API Reference](https://learn.microsoft.com/power-apps/developer/data-platform/webapi/)** | Detailed Web API reference and examples | +| **[Dataverse Web API Reference](https://learn.microsoft.com/power-apps/developer/data-platform/webapi/)** | Detailed Web API reference and examples | | **[Azure Identity for Python](https://learn.microsoft.com/python/api/overview/azure/identity-readme)** | Authentication library documentation and credential types | | **[Power Platform Developer Center](https://learn.microsoft.com/power-platform/developer/)** | Broader Power Platform development resources | | **[Dataverse SDK for .NET](https://learn.microsoft.com/power-apps/developer/data-platform/org-service/overview)** | Official .NET SDK for Microsoft Dataverse | diff --git a/src/PowerPlatform/Dataverse/claude_skill/dataverse-sdk-use/SKILL.md b/src/PowerPlatform/Dataverse/claude_skill/dataverse-sdk-use/SKILL.md index d25815d7..a3c5ebc1 100644 --- a/src/PowerPlatform/Dataverse/claude_skill/dataverse-sdk-use/SKILL.md +++ b/src/PowerPlatform/Dataverse/claude_skill/dataverse-sdk-use/SKILL.md @@ -588,6 +588,115 @@ except ValidationError as e: 10. **Test in non-production environments** first 11. **Use named constants** - Import cascade behavior constants from `PowerPlatform.Dataverse.common.constants` +## Async Client + +The SDK ships a full async client, `AsyncDataverseClient`, under `PowerPlatform.Dataverse.aio`. Requires the `[async]` extra: `pip install "PowerPlatform-Dataverse-Client[async]"`. + +### Import +```python +from azure.identity.aio import InteractiveBrowserCredential # or ClientSecretCredential, etc. +from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient +``` + +### Client Initialization +```python +# Context manager (recommended -- closes session and clears caches automatically) +async with AsyncDataverseClient("https://yourorg.crm.dynamics.com", credential) as client: + ... # all operations here + +# Standalone (call aclose() in a finally block) +client = AsyncDataverseClient("https://yourorg.crm.dynamics.com", credential) +try: + ... +finally: + await client.aclose() +``` + +### CRUD Operations +Every sync method has an async equivalent -- add `await`: +```python +# Create +account_id = await client.records.create("account", {"name": "Contoso Ltd"}) + +# Read +account = await client.records.retrieve("account", account_id, select=["name", "telephone1"]) + +# Update +await client.records.update("account", account_id, {"telephone1": "555-0200"}) + +# Delete +await client.records.delete("account", account_id) + +# Bulk create +ids = await client.records.create("account", [{"name": "A"}, {"name": "B"}]) +``` + +### Query Builder +```python +from PowerPlatform.Dataverse.models.filters import col + +# Collect all results +result = await ( + client.query.builder("account") + .select("name", "telephone1") + .where(col("statecode") == 0) + .top(10) + .execute() +) +for record in result: + print(record["name"]) + +# Lazy page iteration (memory-efficient) +async for page in ( + client.query.builder("account") + .select("name") + .page_size(500) + .execute_pages() +): + for record in page: + print(record["name"]) + +# SQL query +rows = await client.query.sql("SELECT TOP 5 name FROM account") + +# FetchXML +xml = '' +rows = await client.query.fetchxml(xml).execute() +``` + +### Batch and Changesets +```python +# Plain batch +batch = client.batch.new() +batch.records.create("account", {"name": "Alpha"}) +result = await batch.execute() + +# Atomic changeset +batch = client.batch.new() +async with batch.changeset() as cs: + ref = cs.records.create("contact", {"firstname": "Alice"}) + cs.records.update("account", account_id, {"primarycontactid@odata.bind": ref}) +result = await batch.execute() +``` + +### DataFrame Operations +```python +import pandas as pd + +# Query to DataFrame +result = await ( + client.query.builder("account") + .select("name", "telephone1") + .where(col("statecode") == 0) + .execute() +) +df = result.to_dataframe() + +# Create from DataFrame +new_accounts = pd.DataFrame([{"name": "Contoso"}, {"name": "Fabrikam"}]) +ids = await client.dataframe.create("account", new_accounts) +``` + ## Additional Resources Load these resources as needed during development: From 774102a96a51ec888dcc5b074b0fa712dbbbd4e5 Mon Sep 17 00:00:00 2001 From: Abel Milash Date: Tue, 19 May 2026 17:36:58 -0700 Subject: [PATCH 07/34] Simplify async _async_odata.py: replace multi-pass comprehensions with for loops Five methods used [await ... for x in xs] list comprehensions followed by zip or a second pass, running coroutines sequentially but with unnecessary complexity. Replaced with plain for loops that match the sync _odata.py structure exactly: _upsert_multiple, _build_create_multiple, _build_update_multiple_from_records, _build_upsert_multiple, _delete_columns. Co-Authored-By: Claude Sonnet 4.6 --- .../Dataverse/aio/data/_async_odata.py | 45 +++++++++---------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/src/PowerPlatform/Dataverse/aio/data/_async_odata.py b/src/PowerPlatform/Dataverse/aio/data/_async_odata.py index 08bd9327..906ffb4c 100644 --- a/src/PowerPlatform/Dataverse/aio/data/_async_odata.py +++ b/src/PowerPlatform/Dataverse/aio/data/_async_odata.py @@ -365,11 +365,11 @@ async def _upsert_multiple( f"alternate_keys and records must have the same length " f"({len(alternate_keys)} != {len(records)})" ) logical_name = table_schema_name.lower() - lowered_records = [self._lowercase_keys(r) for r in records] - converted = [await self._convert_labels_to_ints(table_schema_name, r) for r in lowered_records] targets: List[Dict[str, Any]] = [] - for alt_key, record_processed in zip(alternate_keys, converted): + for alt_key, record in zip(alternate_keys, records): alt_key_lower = self._lowercase_keys(alt_key) + record_processed = self._lowercase_keys(record) + record_processed = await self._convert_labels_to_ints(table_schema_name, record_processed) conflicting = { k for k in set(alt_key_lower) & set(record_processed) if alt_key_lower[k] != record_processed[k] } @@ -1475,11 +1475,8 @@ async def _delete_columns( deleted: List[str] = [] needs_picklist_flush = False - attr_metas = [ - await self._get_attribute_metadata(metadata_id, col, extra_select="@odata.type,AttributeType") - for col in names - ] - for column_name, attr_meta in zip(names, attr_metas): + for column_name in names: + attr_meta = await self._get_attribute_metadata(metadata_id, column_name, extra_select="@odata.type,AttributeType") if not attr_meta: raise MetadataError( f"Column '{column_name}' not found on table '{entity_schema}'.", @@ -1535,12 +1532,13 @@ async def _build_create_multiple( if not all(isinstance(r, dict) for r in records): raise TypeError("All items for multi-create must be dicts") logical_name = table.lower() - lowered = [self._lowercase_keys(r) for r in records] - converted = [await self._convert_labels_to_ints(table, r) for r in lowered] - enriched = [ - {**r, "@odata.type": f"Microsoft.Dynamics.CRM.{logical_name}"} if "@odata.type" not in r else r - for r in converted - ] + enriched = [] + for r in records: + r = self._lowercase_keys(r) + r = await self._convert_labels_to_ints(table, r) + if "@odata.type" not in r: + r = {**r, "@odata.type": f"Microsoft.Dynamics.CRM.{logical_name}"} + enriched.append(r) return _RawRequest( method="POST", url=f"{self.api}/{entity_set}/Microsoft.Dynamics.CRM.CreateMultiple", @@ -1588,12 +1586,13 @@ async def _build_update_multiple_from_records( :meth:`_build_update_multiple` (which assembles from ids + changes). """ logical_name = table.lower() - lowered = [self._lowercase_keys(r) for r in records] - converted = [await self._convert_labels_to_ints(table, r) for r in lowered] - enriched = [ - {**r, "@odata.type": f"Microsoft.Dynamics.CRM.{logical_name}"} if "@odata.type" not in r else r - for r in converted - ] + enriched = [] + for r in records: + r = self._lowercase_keys(r) + r = await self._convert_labels_to_ints(table, r) + if "@odata.type" not in r: + r = {**r, "@odata.type": f"Microsoft.Dynamics.CRM.{logical_name}"} + enriched.append(r) return _RawRequest( method="POST", url=f"{self.api}/{entity_set}/Microsoft.Dynamics.CRM.UpdateMultiple", @@ -1658,11 +1657,11 @@ async def _build_upsert_multiple( subcode="upsert_length_mismatch", ) logical_name = table.lower() - lowered_records = [self._lowercase_keys(r) for r in records] - converted = [await self._convert_labels_to_ints(table, r) for r in lowered_records] targets: List[Dict[str, Any]] = [] - for alt_key, record_processed in zip(alternate_keys, converted): + for alt_key, record in zip(alternate_keys, records): alt_key_lower = self._lowercase_keys(alt_key) + record_processed = self._lowercase_keys(record) + record_processed = await self._convert_labels_to_ints(table, record_processed) conflicting = { k for k in set(alt_key_lower) & set(record_processed) if alt_key_lower[k] != record_processed[k] } From 170b550411d252eef707ade400f52eada1d79a41 Mon Sep 17 00:00:00 2001 From: Abel Milash Date: Tue, 19 May 2026 17:44:51 -0700 Subject: [PATCH 08/34] Fix black formatting in _async_odata.py; sync dev SKILL copies Apply black formatting to _async_odata.py (required by CI). Add async client item 7 to .claude/skills/dataverse-sdk-dev/SKILL.md to match the src/ copy updated in the previous commit. Co-Authored-By: Claude Sonnet 4.6 --- .claude/skills/dataverse-sdk-dev/SKILL.md | 1 + src/PowerPlatform/Dataverse/aio/data/_async_odata.py | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.claude/skills/dataverse-sdk-dev/SKILL.md b/.claude/skills/dataverse-sdk-dev/SKILL.md index c3af44c5..cab1f131 100644 --- a/.claude/skills/dataverse-sdk-dev/SKILL.md +++ b/.claude/skills/dataverse-sdk-dev/SKILL.md @@ -19,6 +19,7 @@ This skill provides guidance for developers working on the PowerPlatform Dataver 4. **Update documentation** when adding features - Keep README and SKILL files (both copies) in sync 5. **Consider backwards compatibility** - Avoid breaking changes 6. **Internal vs public naming** - Modules, files, and functions not meant to be part of the public API must use a `_` prefix (e.g., `_odata.py`, `_relationships.py`). Files without the prefix (e.g., `constants.py`, `metadata.py`) are public and importable by SDK consumers +7. **Async client** - The SDK ships a full async client (`AsyncDataverseClient`) under `src/PowerPlatform/Dataverse/aio/`. When adding a feature to the sync client, add it to the async client too. The async operation namespaces mirror the sync ones: `aio/operations/async_records.py`, `async_query.py`, `async_tables.py`, `async_batch.py`, `async_files.py`. Pure logic (payload builders, URL construction) goes in `data/_odata_base.py` — inherited by both `_ODataClient` and `_AsyncODataClient` — so it only needs to be written once; HTTP-calling code goes in `data/_odata.py` (sync) or `aio/data/_async_odata.py` (async). Async tests live in `tests/unit/aio/` and async examples in `examples/aio/`. The `aiohttp` dependency is an optional extra (`pip install "PowerPlatform-Dataverse-Client[async]"`) — do not move it into the required `dependencies` list in `pyproject.toml`. ### Dataverse Property Naming Rules diff --git a/src/PowerPlatform/Dataverse/aio/data/_async_odata.py b/src/PowerPlatform/Dataverse/aio/data/_async_odata.py index 906ffb4c..fa3ea0dc 100644 --- a/src/PowerPlatform/Dataverse/aio/data/_async_odata.py +++ b/src/PowerPlatform/Dataverse/aio/data/_async_odata.py @@ -1476,7 +1476,9 @@ async def _delete_columns( needs_picklist_flush = False for column_name in names: - attr_meta = await self._get_attribute_metadata(metadata_id, column_name, extra_select="@odata.type,AttributeType") + attr_meta = await self._get_attribute_metadata( + metadata_id, column_name, extra_select="@odata.type,AttributeType" + ) if not attr_meta: raise MetadataError( f"Column '{column_name}' not found on table '{entity_schema}'.", From 836ca29cb7f23dd596f8926b340d78865288eefd Mon Sep 17 00:00:00 2001 From: Abel Milash Date: Tue, 19 May 2026 21:41:57 -0700 Subject: [PATCH 09/34] Fix azure.identity import: InteractiveBrowserCredential is not in aio namespace azure.identity.aio does not export InteractiveBrowserCredential (it is sync-only). Use azure.identity.InteractiveBrowserCredential for dev/interactive use. Also drop the erroneous `await credential.close()` call in the README quick start (sync credential, no coroutine to await). Co-Authored-By: Claude Sonnet 4.6 --- .claude/skills/dataverse-sdk-use/SKILL.md | 3 ++- README.md | 25 ++++++++----------- .../Dataverse/aio/async_client.py | 2 +- .../claude_skill/dataverse-sdk-use/SKILL.md | 3 ++- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/.claude/skills/dataverse-sdk-use/SKILL.md b/.claude/skills/dataverse-sdk-use/SKILL.md index a3c5ebc1..0de015fc 100644 --- a/.claude/skills/dataverse-sdk-use/SKILL.md +++ b/.claude/skills/dataverse-sdk-use/SKILL.md @@ -594,7 +594,8 @@ The SDK ships a full async client, `AsyncDataverseClient`, under `PowerPlatform. ### Import ```python -from azure.identity.aio import InteractiveBrowserCredential # or ClientSecretCredential, etc. +from azure.identity import InteractiveBrowserCredential # development (sync, works with async client) +from azure.identity.aio import ClientSecretCredential # production (native async) from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient ``` diff --git a/README.md b/README.md index 777884ce..02e8b68f 100644 --- a/README.md +++ b/README.md @@ -800,24 +800,21 @@ pip install "PowerPlatform-Dataverse-Client[async]" ```python import asyncio -from azure.identity.aio import InteractiveBrowserCredential +from azure.identity import InteractiveBrowserCredential from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient async def main(): credential = InteractiveBrowserCredential() - try: - async with AsyncDataverseClient("https://yourorg.crm.dynamics.com", credential) as client: - # Create a contact - contact_id = await client.records.create("contact", {"firstname": "John", "lastname": "Doe"}) - - # Read it back - contact = await client.records.retrieve("contact", contact_id, select=["firstname", "lastname"]) - print(f"Created: {contact['firstname']} {contact['lastname']}") - - # Clean up - await client.records.delete("contact", contact_id) - finally: - await credential.close() + async with AsyncDataverseClient("https://yourorg.crm.dynamics.com", credential) as client: + # Create a contact + contact_id = await client.records.create("contact", {"firstname": "John", "lastname": "Doe"}) + + # Read it back + contact = await client.records.retrieve("contact", contact_id, select=["firstname", "lastname"]) + print(f"Created: {contact['firstname']} {contact['lastname']}") + + # Clean up + await client.records.delete("contact", contact_id) asyncio.run(main()) ``` diff --git a/src/PowerPlatform/Dataverse/aio/async_client.py b/src/PowerPlatform/Dataverse/aio/async_client.py index 30553883..5dd9abb7 100644 --- a/src/PowerPlatform/Dataverse/aio/async_client.py +++ b/src/PowerPlatform/Dataverse/aio/async_client.py @@ -77,7 +77,7 @@ class AsyncDataverseClient: Example: **Recommended -- async context manager** (enables HTTP connection pooling):: - from azure.identity.aio import InteractiveBrowserCredential + from azure.identity import InteractiveBrowserCredential from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient credential = InteractiveBrowserCredential() diff --git a/src/PowerPlatform/Dataverse/claude_skill/dataverse-sdk-use/SKILL.md b/src/PowerPlatform/Dataverse/claude_skill/dataverse-sdk-use/SKILL.md index a3c5ebc1..0de015fc 100644 --- a/src/PowerPlatform/Dataverse/claude_skill/dataverse-sdk-use/SKILL.md +++ b/src/PowerPlatform/Dataverse/claude_skill/dataverse-sdk-use/SKILL.md @@ -594,7 +594,8 @@ The SDK ships a full async client, `AsyncDataverseClient`, under `PowerPlatform. ### Import ```python -from azure.identity.aio import InteractiveBrowserCredential # or ClientSecretCredential, etc. +from azure.identity import InteractiveBrowserCredential # development (sync, works with async client) +from azure.identity.aio import ClientSecretCredential # production (native async) from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient ``` From 7a360c1381ec5fdb3702c5b62b36bb5378e49c10 Mon Sep 17 00:00:00 2001 From: Abel Milash Date: Tue, 19 May 2026 21:44:18 -0700 Subject: [PATCH 10/34] Fix async credential examples: use ClientSecretCredential from azure.identity.aio InteractiveBrowserCredential (azure.identity) is sync-only and cannot be passed directly to AsyncDataverseClient, which requires async def get_token(). Update README quick start, both SKILL copies, and async_client.py docstring to use ClientSecretCredential from azure.identity.aio. Add note pointing to examples/aio/_auth.py for the interactive browser wrapper used in examples. Co-Authored-By: Claude Sonnet 4.6 --- .claude/skills/dataverse-sdk-use/SKILL.md | 7 ++++-- README.md | 24 +++++++++++-------- .../Dataverse/aio/async_client.py | 11 ++++----- .../claude_skill/dataverse-sdk-use/SKILL.md | 7 ++++-- 4 files changed, 29 insertions(+), 20 deletions(-) diff --git a/.claude/skills/dataverse-sdk-use/SKILL.md b/.claude/skills/dataverse-sdk-use/SKILL.md index 0de015fc..6d57736b 100644 --- a/.claude/skills/dataverse-sdk-use/SKILL.md +++ b/.claude/skills/dataverse-sdk-use/SKILL.md @@ -594,8 +594,11 @@ The SDK ships a full async client, `AsyncDataverseClient`, under `PowerPlatform. ### Import ```python -from azure.identity import InteractiveBrowserCredential # development (sync, works with async client) -from azure.identity.aio import ClientSecretCredential # production (native async) +# AsyncDataverseClient requires an async-compatible credential (async def get_token()) +from azure.identity.aio import ClientSecretCredential # production +from azure.identity.aio import ManagedIdentityCredential # production (managed identity) +# For interactive browser (development), use the AsyncInteractiveBrowserCredential wrapper +# from examples/aio/_auth.py — azure.identity.InteractiveBrowserCredential is sync-only from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient ``` diff --git a/README.md b/README.md index 02e8b68f..6044b536 100644 --- a/README.md +++ b/README.md @@ -798,27 +798,31 @@ pip install "PowerPlatform-Dataverse-Client[async]" ### Quick start +`AsyncDataverseClient` requires an async-compatible credential (`async def get_token()`). Use credentials from `azure.identity.aio` for production, or `AzureCliCredential` for local development: + ```python import asyncio -from azure.identity import InteractiveBrowserCredential +from azure.identity.aio import ClientSecretCredential from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient async def main(): - credential = InteractiveBrowserCredential() - async with AsyncDataverseClient("https://yourorg.crm.dynamics.com", credential) as client: - # Create a contact - contact_id = await client.records.create("contact", {"firstname": "John", "lastname": "Doe"}) + async with ClientSecretCredential(tenant_id, client_id, client_secret) as credential: + async with AsyncDataverseClient("https://yourorg.crm.dynamics.com", credential) as client: + # Create a contact + contact_id = await client.records.create("contact", {"firstname": "John", "lastname": "Doe"}) - # Read it back - contact = await client.records.retrieve("contact", contact_id, select=["firstname", "lastname"]) - print(f"Created: {contact['firstname']} {contact['lastname']}") + # Read it back + contact = await client.records.retrieve("contact", contact_id, select=["firstname", "lastname"]) + print(f"Created: {contact['firstname']} {contact['lastname']}") - # Clean up - await client.records.delete("contact", contact_id) + # Clean up + await client.records.delete("contact", contact_id) asyncio.run(main()) ``` +> **Interactive browser (development):** `InteractiveBrowserCredential` from `azure.identity` is sync-only and cannot be passed directly. See [examples/aio/_auth.py](https://github.com/microsoft/PowerPlatform-DataverseClient-Python/blob/main/examples/aio/_auth.py) for an async wrapper used by the example scripts. + ### Standalone usage (without `async with`) ```python diff --git a/src/PowerPlatform/Dataverse/aio/async_client.py b/src/PowerPlatform/Dataverse/aio/async_client.py index 5dd9abb7..ecc883a3 100644 --- a/src/PowerPlatform/Dataverse/aio/async_client.py +++ b/src/PowerPlatform/Dataverse/aio/async_client.py @@ -77,14 +77,13 @@ class AsyncDataverseClient: Example: **Recommended -- async context manager** (enables HTTP connection pooling):: - from azure.identity import InteractiveBrowserCredential + from azure.identity.aio import ClientSecretCredential from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient - credential = InteractiveBrowserCredential() - - async with AsyncDataverseClient("https://org.crm.dynamics.com", credential) as client: - record_id = await client.records.create("account", {"name": "Contoso Ltd"}) - await client.records.update("account", record_id, {"telephone1": "555-0100"}) + async with ClientSecretCredential(tenant_id, client_id, client_secret) as credential: + async with AsyncDataverseClient("https://org.crm.dynamics.com", credential) as client: + record_id = await client.records.create("account", {"name": "Contoso Ltd"}) + await client.records.update("account", record_id, {"telephone1": "555-0100"}) # Session closed, caches cleared automatically **Manual lifecycle**:: diff --git a/src/PowerPlatform/Dataverse/claude_skill/dataverse-sdk-use/SKILL.md b/src/PowerPlatform/Dataverse/claude_skill/dataverse-sdk-use/SKILL.md index 0de015fc..6d57736b 100644 --- a/src/PowerPlatform/Dataverse/claude_skill/dataverse-sdk-use/SKILL.md +++ b/src/PowerPlatform/Dataverse/claude_skill/dataverse-sdk-use/SKILL.md @@ -594,8 +594,11 @@ The SDK ships a full async client, `AsyncDataverseClient`, under `PowerPlatform. ### Import ```python -from azure.identity import InteractiveBrowserCredential # development (sync, works with async client) -from azure.identity.aio import ClientSecretCredential # production (native async) +# AsyncDataverseClient requires an async-compatible credential (async def get_token()) +from azure.identity.aio import ClientSecretCredential # production +from azure.identity.aio import ManagedIdentityCredential # production (managed identity) +# For interactive browser (development), use the AsyncInteractiveBrowserCredential wrapper +# from examples/aio/_auth.py — azure.identity.InteractiveBrowserCredential is sync-only from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient ``` From 5c6bdc73d1a47400f4ed92f3c1e291b5f98174c7 Mon Sep 17 00:00:00 2001 From: Abel Milash Date: Tue, 19 May 2026 21:46:46 -0700 Subject: [PATCH 11/34] Use DefaultAzureCredential in async examples and docs Replaces ClientSecretCredential with DefaultAzureCredential in README quick start, both SKILL copies, and async_client.py docstring. DefaultAzureCredential is simpler for quick starts and works across dev and production environments without hardcoding credentials. Co-Authored-By: Claude Sonnet 4.6 --- .claude/skills/dataverse-sdk-use/SKILL.md | 4 ++-- README.md | 4 ++-- src/PowerPlatform/Dataverse/aio/async_client.py | 4 ++-- .../Dataverse/claude_skill/dataverse-sdk-use/SKILL.md | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.claude/skills/dataverse-sdk-use/SKILL.md b/.claude/skills/dataverse-sdk-use/SKILL.md index 6d57736b..abaa1297 100644 --- a/.claude/skills/dataverse-sdk-use/SKILL.md +++ b/.claude/skills/dataverse-sdk-use/SKILL.md @@ -595,8 +595,8 @@ The SDK ships a full async client, `AsyncDataverseClient`, under `PowerPlatform. ### Import ```python # AsyncDataverseClient requires an async-compatible credential (async def get_token()) -from azure.identity.aio import ClientSecretCredential # production -from azure.identity.aio import ManagedIdentityCredential # production (managed identity) +from azure.identity.aio import DefaultAzureCredential # works in most environments +from azure.identity.aio import ClientSecretCredential # explicit service principal # For interactive browser (development), use the AsyncInteractiveBrowserCredential wrapper # from examples/aio/_auth.py — azure.identity.InteractiveBrowserCredential is sync-only from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient diff --git a/README.md b/README.md index 6044b536..388320ce 100644 --- a/README.md +++ b/README.md @@ -802,11 +802,11 @@ pip install "PowerPlatform-Dataverse-Client[async]" ```python import asyncio -from azure.identity.aio import ClientSecretCredential +from azure.identity.aio import DefaultAzureCredential from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient async def main(): - async with ClientSecretCredential(tenant_id, client_id, client_secret) as credential: + async with DefaultAzureCredential() as credential: async with AsyncDataverseClient("https://yourorg.crm.dynamics.com", credential) as client: # Create a contact contact_id = await client.records.create("contact", {"firstname": "John", "lastname": "Doe"}) diff --git a/src/PowerPlatform/Dataverse/aio/async_client.py b/src/PowerPlatform/Dataverse/aio/async_client.py index ecc883a3..5c47c121 100644 --- a/src/PowerPlatform/Dataverse/aio/async_client.py +++ b/src/PowerPlatform/Dataverse/aio/async_client.py @@ -77,10 +77,10 @@ class AsyncDataverseClient: Example: **Recommended -- async context manager** (enables HTTP connection pooling):: - from azure.identity.aio import ClientSecretCredential + from azure.identity.aio import DefaultAzureCredential from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient - async with ClientSecretCredential(tenant_id, client_id, client_secret) as credential: + async with DefaultAzureCredential() as credential: async with AsyncDataverseClient("https://org.crm.dynamics.com", credential) as client: record_id = await client.records.create("account", {"name": "Contoso Ltd"}) await client.records.update("account", record_id, {"telephone1": "555-0100"}) diff --git a/src/PowerPlatform/Dataverse/claude_skill/dataverse-sdk-use/SKILL.md b/src/PowerPlatform/Dataverse/claude_skill/dataverse-sdk-use/SKILL.md index 6d57736b..abaa1297 100644 --- a/src/PowerPlatform/Dataverse/claude_skill/dataverse-sdk-use/SKILL.md +++ b/src/PowerPlatform/Dataverse/claude_skill/dataverse-sdk-use/SKILL.md @@ -595,8 +595,8 @@ The SDK ships a full async client, `AsyncDataverseClient`, under `PowerPlatform. ### Import ```python # AsyncDataverseClient requires an async-compatible credential (async def get_token()) -from azure.identity.aio import ClientSecretCredential # production -from azure.identity.aio import ManagedIdentityCredential # production (managed identity) +from azure.identity.aio import DefaultAzureCredential # works in most environments +from azure.identity.aio import ClientSecretCredential # explicit service principal # For interactive browser (development), use the AsyncInteractiveBrowserCredential wrapper # from examples/aio/_auth.py — azure.identity.InteractiveBrowserCredential is sync-only from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient From 0d71bd36e11bcf2c9b68d9879f66ee885a5f3ef6 Mon Sep 17 00:00:00 2001 From: Abel Milash Date: Tue, 19 May 2026 21:48:22 -0700 Subject: [PATCH 12/34] Simplify async auth docs: single import with one-line note on interactive Co-Authored-By: Claude Sonnet 4.6 --- .claude/skills/dataverse-sdk-use/SKILL.md | 6 +----- README.md | 4 +--- .../Dataverse/claude_skill/dataverse-sdk-use/SKILL.md | 6 +----- 3 files changed, 3 insertions(+), 13 deletions(-) diff --git a/.claude/skills/dataverse-sdk-use/SKILL.md b/.claude/skills/dataverse-sdk-use/SKILL.md index abaa1297..1da6810b 100644 --- a/.claude/skills/dataverse-sdk-use/SKILL.md +++ b/.claude/skills/dataverse-sdk-use/SKILL.md @@ -594,11 +594,7 @@ The SDK ships a full async client, `AsyncDataverseClient`, under `PowerPlatform. ### Import ```python -# AsyncDataverseClient requires an async-compatible credential (async def get_token()) -from azure.identity.aio import DefaultAzureCredential # works in most environments -from azure.identity.aio import ClientSecretCredential # explicit service principal -# For interactive browser (development), use the AsyncInteractiveBrowserCredential wrapper -# from examples/aio/_auth.py — azure.identity.InteractiveBrowserCredential is sync-only +from azure.identity.aio import DefaultAzureCredential # InteractiveBrowserCredential is sync-only; use DefaultAzureCredential or azure.identity.aio equivalents from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient ``` diff --git a/README.md b/README.md index 388320ce..f092054d 100644 --- a/README.md +++ b/README.md @@ -798,8 +798,6 @@ pip install "PowerPlatform-Dataverse-Client[async]" ### Quick start -`AsyncDataverseClient` requires an async-compatible credential (`async def get_token()`). Use credentials from `azure.identity.aio` for production, or `AzureCliCredential` for local development: - ```python import asyncio from azure.identity.aio import DefaultAzureCredential @@ -821,7 +819,7 @@ async def main(): asyncio.run(main()) ``` -> **Interactive browser (development):** `InteractiveBrowserCredential` from `azure.identity` is sync-only and cannot be passed directly. See [examples/aio/_auth.py](https://github.com/microsoft/PowerPlatform-DataverseClient-Python/blob/main/examples/aio/_auth.py) for an async wrapper used by the example scripts. +> **Note:** `InteractiveBrowserCredential` from `azure.identity` is sync-only and cannot be used directly with the async client. See [examples/aio/_auth.py](https://github.com/microsoft/PowerPlatform-DataverseClient-Python/blob/main/examples/aio/_auth.py) for an async wrapper. ### Standalone usage (without `async with`) diff --git a/src/PowerPlatform/Dataverse/claude_skill/dataverse-sdk-use/SKILL.md b/src/PowerPlatform/Dataverse/claude_skill/dataverse-sdk-use/SKILL.md index abaa1297..1da6810b 100644 --- a/src/PowerPlatform/Dataverse/claude_skill/dataverse-sdk-use/SKILL.md +++ b/src/PowerPlatform/Dataverse/claude_skill/dataverse-sdk-use/SKILL.md @@ -594,11 +594,7 @@ The SDK ships a full async client, `AsyncDataverseClient`, under `PowerPlatform. ### Import ```python -# AsyncDataverseClient requires an async-compatible credential (async def get_token()) -from azure.identity.aio import DefaultAzureCredential # works in most environments -from azure.identity.aio import ClientSecretCredential # explicit service principal -# For interactive browser (development), use the AsyncInteractiveBrowserCredential wrapper -# from examples/aio/_auth.py — azure.identity.InteractiveBrowserCredential is sync-only +from azure.identity.aio import DefaultAzureCredential # InteractiveBrowserCredential is sync-only; use DefaultAzureCredential or azure.identity.aio equivalents from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient ``` From 255a798959855fb874f115c5ee5cecd51d406c47 Mon Sep 17 00:00:00 2001 From: Abel Milash Date: Tue, 19 May 2026 21:50:24 -0700 Subject: [PATCH 13/34] Remove inline auth comment from async import examples Co-Authored-By: Claude Sonnet 4.6 --- .claude/skills/dataverse-sdk-use/SKILL.md | 2 +- .../Dataverse/claude_skill/dataverse-sdk-use/SKILL.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.claude/skills/dataverse-sdk-use/SKILL.md b/.claude/skills/dataverse-sdk-use/SKILL.md index 1da6810b..34ed35c3 100644 --- a/.claude/skills/dataverse-sdk-use/SKILL.md +++ b/.claude/skills/dataverse-sdk-use/SKILL.md @@ -594,7 +594,7 @@ The SDK ships a full async client, `AsyncDataverseClient`, under `PowerPlatform. ### Import ```python -from azure.identity.aio import DefaultAzureCredential # InteractiveBrowserCredential is sync-only; use DefaultAzureCredential or azure.identity.aio equivalents +from azure.identity.aio import DefaultAzureCredential from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient ``` diff --git a/src/PowerPlatform/Dataverse/claude_skill/dataverse-sdk-use/SKILL.md b/src/PowerPlatform/Dataverse/claude_skill/dataverse-sdk-use/SKILL.md index 1da6810b..34ed35c3 100644 --- a/src/PowerPlatform/Dataverse/claude_skill/dataverse-sdk-use/SKILL.md +++ b/src/PowerPlatform/Dataverse/claude_skill/dataverse-sdk-use/SKILL.md @@ -594,7 +594,7 @@ The SDK ships a full async client, `AsyncDataverseClient`, under `PowerPlatform. ### Import ```python -from azure.identity.aio import DefaultAzureCredential # InteractiveBrowserCredential is sync-only; use DefaultAzureCredential or azure.identity.aio equivalents +from azure.identity.aio import DefaultAzureCredential from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient ``` From f37b9b4a21b96e71e5941bbf59c7649aec1c5c81 Mon Sep 17 00:00:00 2001 From: Abel Milash Date: Tue, 19 May 2026 22:02:22 -0700 Subject: [PATCH 14/34] Rename 'materialized' to 'response' in _async_http.py for clarity Co-Authored-By: Claude Sonnet 4.6 --- src/PowerPlatform/Dataverse/aio/core/_async_http.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/PowerPlatform/Dataverse/aio/core/_async_http.py b/src/PowerPlatform/Dataverse/aio/core/_async_http.py index 402be8d4..5c6e8d25 100644 --- a/src/PowerPlatform/Dataverse/aio/core/_async_http.py +++ b/src/PowerPlatform/Dataverse/aio/core/_async_http.py @@ -144,22 +144,22 @@ async def _request(self, method: str, url: str, **kwargs: Any) -> _AsyncResponse t0 = time.monotonic() async with self._session.request(method, url, **kwargs) as resp: body = await resp.read() - materialized = _AsyncResponse(resp.status, dict(resp.headers), body) + response = _AsyncResponse(resp.status, dict(resp.headers), body) elapsed_ms = (time.monotonic() - t0) * 1000 if self._logger is not None: # Only decode text when body logging is enabled — avoids # unnecessary overhead for large payloads when max_body_bytes == 0. - resp_body = materialized.text if self._logger.body_logging_enabled else None + resp_body = response.text if self._logger.body_logging_enabled else None self._logger.log_response( method, url, - status_code=materialized.status, - headers=materialized.headers, + status_code=response.status, + headers=response.headers, body=resp_body, elapsed_ms=elapsed_ms, ) - return materialized + return response except (aiohttp.ClientError, asyncio.TimeoutError) as exc: if self._logger is not None: self._logger.log_error( From 58fe82e5bb42eba94f649268fc57f48d473ea973 Mon Sep 17 00:00:00 2001 From: Abel Milash Date: Tue, 19 May 2026 22:09:51 -0700 Subject: [PATCH 15/34] Fix _async_batch.py: rename r to response, simplify remove_columns loop Rename `r` to `response` for consistency with _async_http.py. Replace multi-pass comprehension + zip in _resolve_table_remove_columns with a plain for loop matching the sync _batch.py structure. Co-Authored-By: Claude Sonnet 4.6 --- .../Dataverse/aio/data/_async_batch.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/PowerPlatform/Dataverse/aio/data/_async_batch.py b/src/PowerPlatform/Dataverse/aio/data/_async_batch.py index 967929dd..ad12cfa8 100644 --- a/src/PowerPlatform/Dataverse/aio/data/_async_batch.py +++ b/src/PowerPlatform/Dataverse/aio/data/_async_batch.py @@ -97,7 +97,7 @@ async def execute( headers["Prefer"] = "odata.continue-on-error" url = f"{self._od.api}/$batch" - r = await self._od._request( + response = await self._od._request( "post", url, data=body.encode("utf-8"), @@ -109,7 +109,7 @@ async def execute( expected=(200, 202, 207, 400), ) - return self._parse_batch_response(r) + return self._parse_batch_response(response) # ------------------------------------------------------------------ # Intent resolution dispatcher @@ -273,12 +273,11 @@ async def _resolve_table_add_columns(self, op: _TableAddColumns) -> List[_RawReq async def _resolve_table_remove_columns(self, op: _TableRemoveColumns) -> List[_RawRequest]: columns = [op.columns] if isinstance(op.columns, str) else list(op.columns) metadata_id = await self._require_entity_metadata(op.table) - attr_metas = [ - await self._od._get_attribute_metadata(metadata_id, col_name, extra_select="@odata.type,AttributeType") - for col_name in columns - ] requests: List[_RawRequest] = [] - for col_name, attr_meta in zip(columns, attr_metas): + for col_name in columns: + attr_meta = await self._od._get_attribute_metadata( + metadata_id, col_name, extra_select="@odata.type,AttributeType" + ) if not attr_meta or not attr_meta.get("MetadataId"): raise MetadataError( f"Column '{col_name}' not found on table '{op.table}'.", From bd74d022767a8f3b021f9724e60dcb5b6b3671d5 Mon Sep 17 00:00:00 2001 From: Abel Milash Date: Tue, 19 May 2026 22:34:31 -0700 Subject: [PATCH 16/34] Align async fetchxml JSON parsing with sync pattern Replace broad try/except around r.json() with hasattr guard, matching the sync FetchXmlQuery pattern. Remove the now-obsolete test that verified the exception-swallowing behavior. Co-Authored-By: Claude Sonnet 4.6 --- .../Dataverse/aio/models/async_fetchxml_query.py | 5 +---- tests/unit/aio/test_async_query.py | 11 ----------- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/src/PowerPlatform/Dataverse/aio/models/async_fetchxml_query.py b/src/PowerPlatform/Dataverse/aio/models/async_fetchxml_query.py index c10ca6f6..efb20a31 100644 --- a/src/PowerPlatform/Dataverse/aio/models/async_fetchxml_query.py +++ b/src/PowerPlatform/Dataverse/aio/models/async_fetchxml_query.py @@ -99,10 +99,7 @@ async def execute_pages(self) -> AsyncIterator[QueryResult]: headers={"Prefer": _PREFER_HEADER}, params={"fetchXml": current_xml}, ) - try: - data = r.json() - except Exception: - data = {} + data = r.json() if hasattr(r, "json") else {} items = data.get("value") if isinstance(data, dict) else None page_records: List[Record] = [] diff --git a/tests/unit/aio/test_async_query.py b/tests/unit/aio/test_async_query.py index 3d867bd3..6ad1b507 100644 --- a/tests/unit/aio/test_async_query.py +++ b/tests/unit/aio/test_async_query.py @@ -500,17 +500,6 @@ async def test_execute_multi_page_no_cookie_simple_paging(self, async_client, mo assert len(result) == 2 assert any("simple paging" in str(warning.message) for warning in w) - async def test_execute_json_parse_error_yields_empty_page(self, async_client, mock_od): - """execute() yields an empty page when the response body cannot be parsed as JSON.""" - mock_od._entity_set_from_schema_name = AsyncMock(return_value="accounts") - - resp = MagicMock() - resp.json = MagicMock(side_effect=Exception("invalid json")) - mock_od._request = AsyncMock(return_value=resp) - - result = await async_client.query.fetchxml(_SIMPLE_FETCHXML).execute() - assert len(result) == 0 - async def test_execute_raises_on_max_pages_exceeded(self, async_client, mock_od): """execute() raises ValidationError when paging exceeds the maximum page limit.""" import urllib.parse From d7383775105106cfae18b198cd523e4a4a5b7298 Mon Sep 17 00:00:00 2001 From: Abel Milash Date: Tue, 19 May 2026 23:07:27 -0700 Subject: [PATCH 17/34] Reorder fetchxml() in async_query.py to match sync method order Moves fetchxml() after sql_columns() to match the order in the sync QueryOperations: builder, sql, fetchxml, sql_columns, odata_*. Co-Authored-By: Claude Sonnet 4.6 --- .../Dataverse/aio/operations/async_query.py | 120 +++++++++--------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/src/PowerPlatform/Dataverse/aio/operations/async_query.py b/src/PowerPlatform/Dataverse/aio/operations/async_query.py index 7f378380..b4467af7 100644 --- a/src/PowerPlatform/Dataverse/aio/operations/async_query.py +++ b/src/PowerPlatform/Dataverse/aio/operations/async_query.py @@ -93,66 +93,6 @@ def builder(self, table: str) -> AsyncQueryBuilder: qb._query_ops = self return qb - # --------------------------------------------------------------- fetchxml - - def fetchxml(self, xml: str) -> AsyncFetchXmlQuery: - """Return an inert :class:`~PowerPlatform.Dataverse.models.async_fetchxml_query.AsyncFetchXmlQuery` object. - - No HTTP request is made until - :meth:`~PowerPlatform.Dataverse.models.async_fetchxml_query.AsyncFetchXmlQuery.execute` - or - :meth:`~PowerPlatform.Dataverse.models.async_fetchxml_query.AsyncFetchXmlQuery.execute_pages` - is called on the returned object. - - :param xml: Well-formed FetchXML query string. The root ```` - element determines the entity set endpoint. - :type xml: :class:`str` - :return: Inert async query object. - :rtype: :class:`~PowerPlatform.Dataverse.models.async_fetchxml_query.AsyncFetchXmlQuery` - :raises ValidationError: If the FetchXML is not a string, is empty, or exceeds the URL - length limit when encoded. - :raises ValueError: If the FetchXML is missing a root ```` element or name. - - Example:: - - query = client.query.fetchxml(\"\"\" - - - - - - \"\"\") - - # Eager — all pages collected: - result = await query.execute() - df = result.to_dataframe() - - # Lazy — one page at a time: - async for page in query.execute_pages(): - process(page.to_dataframe()) - """ - if not isinstance(xml, str): - raise ValidationError("xml must be a string") - xml = xml.strip() - if not xml: - raise ValidationError("xml must not be empty") - if len(_url_quote(xml, safe="")) > _MAX_URL_LENGTH: - raise ValidationError( - f"FetchXML exceeds the Dataverse URL length limit ({_MAX_URL_LENGTH:,} characters) when encoded. " - "Use a $batch POST request to send FetchXML in the request body where the limit is 64 KB." - ) - try: - root_el = _ET.fromstring(xml) - except _ET.ParseError as exc: - raise ValidationError(f"xml is not well-formed: {exc}") from exc - entity_el = root_el.find("entity") - if entity_el is None: - raise ValueError("FetchXML must contain an child element") - entity_name = entity_el.get("name", "") - if not entity_name: - raise ValueError("FetchXML element must have a 'name' attribute") - return AsyncFetchXmlQuery(xml, entity_name, self._client) - # -------------------------------------------------------------------- sql async def sql(self, sql: str) -> List[Record]: @@ -292,6 +232,66 @@ async def sql_columns( result.sort(key=lambda x: (not x["is_pk"], not x["is_name"], x["name"])) return result + # --------------------------------------------------------------- fetchxml + + def fetchxml(self, xml: str) -> AsyncFetchXmlQuery: + """Return an inert :class:`~PowerPlatform.Dataverse.models.async_fetchxml_query.AsyncFetchXmlQuery` object. + + No HTTP request is made until + :meth:`~PowerPlatform.Dataverse.models.async_fetchxml_query.AsyncFetchXmlQuery.execute` + or + :meth:`~PowerPlatform.Dataverse.models.async_fetchxml_query.AsyncFetchXmlQuery.execute_pages` + is called on the returned object. + + :param xml: Well-formed FetchXML query string. The root ```` + element determines the entity set endpoint. + :type xml: :class:`str` + :return: Inert async query object. + :rtype: :class:`~PowerPlatform.Dataverse.models.async_fetchxml_query.AsyncFetchXmlQuery` + :raises ValidationError: If the FetchXML is not a string, is empty, or exceeds the URL + length limit when encoded. + :raises ValueError: If the FetchXML is missing a root ```` element or name. + + Example:: + + query = client.query.fetchxml(\"\"\" + + + + + + \"\"\") + + # Eager — all pages collected: + result = await query.execute() + df = result.to_dataframe() + + # Lazy — one page at a time: + async for page in query.execute_pages(): + process(page.to_dataframe()) + """ + if not isinstance(xml, str): + raise ValidationError("xml must be a string") + xml = xml.strip() + if not xml: + raise ValidationError("xml must not be empty") + if len(_url_quote(xml, safe="")) > _MAX_URL_LENGTH: + raise ValidationError( + f"FetchXML exceeds the Dataverse URL length limit ({_MAX_URL_LENGTH:,} characters) when encoded. " + "Use a $batch POST request to send FetchXML in the request body where the limit is 64 KB." + ) + try: + root_el = _ET.fromstring(xml) + except _ET.ParseError as exc: + raise ValidationError(f"xml is not well-formed: {exc}") from exc + entity_el = root_el.find("entity") + if entity_el is None: + raise ValueError("FetchXML must contain an child element") + entity_name = entity_el.get("name", "") + if not entity_name: + raise ValueError("FetchXML element must have a 'name' attribute") + return AsyncFetchXmlQuery(xml, entity_name, self._client) + # ========================================================================= # OData helpers -- discover columns, navigation properties, and bind values # ========================================================================= From e4680f897ce88ab36300b8ac185d877de4ab6832 Mon Sep 17 00:00:00 2001 From: Abel Milash Date: Tue, 19 May 2026 23:12:27 -0700 Subject: [PATCH 18/34] Fix fetchxml() position in async_query.py: move above sql_columns Order now matches sync exactly: builder, sql, fetchxml, sql_columns, odata_expands. Co-Authored-By: Claude Sonnet 4.6 --- .../Dataverse/aio/operations/async_query.py | 120 +++++++++--------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/src/PowerPlatform/Dataverse/aio/operations/async_query.py b/src/PowerPlatform/Dataverse/aio/operations/async_query.py index b4467af7..c2daa4e7 100644 --- a/src/PowerPlatform/Dataverse/aio/operations/async_query.py +++ b/src/PowerPlatform/Dataverse/aio/operations/async_query.py @@ -148,6 +148,66 @@ async def sql(self, sql: str) -> List[Record]: rows = await od._query_sql(sql) return [Record.from_api_response("", row) for row in rows] + # --------------------------------------------------------------- fetchxml + + def fetchxml(self, xml: str) -> AsyncFetchXmlQuery: + """Return an inert :class:`~PowerPlatform.Dataverse.models.async_fetchxml_query.AsyncFetchXmlQuery` object. + + No HTTP request is made until + :meth:`~PowerPlatform.Dataverse.models.async_fetchxml_query.AsyncFetchXmlQuery.execute` + or + :meth:`~PowerPlatform.Dataverse.models.async_fetchxml_query.AsyncFetchXmlQuery.execute_pages` + is called on the returned object. + + :param xml: Well-formed FetchXML query string. The root ```` + element determines the entity set endpoint. + :type xml: :class:`str` + :return: Inert async query object. + :rtype: :class:`~PowerPlatform.Dataverse.models.async_fetchxml_query.AsyncFetchXmlQuery` + :raises ValidationError: If the FetchXML is not a string, is empty, or exceeds the URL + length limit when encoded. + :raises ValueError: If the FetchXML is missing a root ```` element or name. + + Example:: + + query = client.query.fetchxml(\"\"\" + + + + + + \"\"\") + + # Eager — all pages collected: + result = await query.execute() + df = result.to_dataframe() + + # Lazy — one page at a time: + async for page in query.execute_pages(): + process(page.to_dataframe()) + """ + if not isinstance(xml, str): + raise ValidationError("xml must be a string") + xml = xml.strip() + if not xml: + raise ValidationError("xml must not be empty") + if len(_url_quote(xml, safe="")) > _MAX_URL_LENGTH: + raise ValidationError( + f"FetchXML exceeds the Dataverse URL length limit ({_MAX_URL_LENGTH:,} characters) when encoded. " + "Use a $batch POST request to send FetchXML in the request body where the limit is 64 KB." + ) + try: + root_el = _ET.fromstring(xml) + except _ET.ParseError as exc: + raise ValidationError(f"xml is not well-formed: {exc}") from exc + entity_el = root_el.find("entity") + if entity_el is None: + raise ValueError("FetchXML must contain an child element") + entity_name = entity_el.get("name", "") + if not entity_name: + raise ValueError("FetchXML element must have a 'name' attribute") + return AsyncFetchXmlQuery(xml, entity_name, self._client) + # --------------------------------------------------------------- sql_columns async def sql_columns( @@ -232,66 +292,6 @@ async def sql_columns( result.sort(key=lambda x: (not x["is_pk"], not x["is_name"], x["name"])) return result - # --------------------------------------------------------------- fetchxml - - def fetchxml(self, xml: str) -> AsyncFetchXmlQuery: - """Return an inert :class:`~PowerPlatform.Dataverse.models.async_fetchxml_query.AsyncFetchXmlQuery` object. - - No HTTP request is made until - :meth:`~PowerPlatform.Dataverse.models.async_fetchxml_query.AsyncFetchXmlQuery.execute` - or - :meth:`~PowerPlatform.Dataverse.models.async_fetchxml_query.AsyncFetchXmlQuery.execute_pages` - is called on the returned object. - - :param xml: Well-formed FetchXML query string. The root ```` - element determines the entity set endpoint. - :type xml: :class:`str` - :return: Inert async query object. - :rtype: :class:`~PowerPlatform.Dataverse.models.async_fetchxml_query.AsyncFetchXmlQuery` - :raises ValidationError: If the FetchXML is not a string, is empty, or exceeds the URL - length limit when encoded. - :raises ValueError: If the FetchXML is missing a root ```` element or name. - - Example:: - - query = client.query.fetchxml(\"\"\" - - - - - - \"\"\") - - # Eager — all pages collected: - result = await query.execute() - df = result.to_dataframe() - - # Lazy — one page at a time: - async for page in query.execute_pages(): - process(page.to_dataframe()) - """ - if not isinstance(xml, str): - raise ValidationError("xml must be a string") - xml = xml.strip() - if not xml: - raise ValidationError("xml must not be empty") - if len(_url_quote(xml, safe="")) > _MAX_URL_LENGTH: - raise ValidationError( - f"FetchXML exceeds the Dataverse URL length limit ({_MAX_URL_LENGTH:,} characters) when encoded. " - "Use a $batch POST request to send FetchXML in the request body where the limit is 64 KB." - ) - try: - root_el = _ET.fromstring(xml) - except _ET.ParseError as exc: - raise ValidationError(f"xml is not well-formed: {exc}") from exc - entity_el = root_el.find("entity") - if entity_el is None: - raise ValueError("FetchXML must contain an child element") - entity_name = entity_el.get("name", "") - if not entity_name: - raise ValueError("FetchXML element must have a 'name' attribute") - return AsyncFetchXmlQuery(xml, entity_name, self._client) - # ========================================================================= # OData helpers -- discover columns, navigation properties, and bind values # ========================================================================= From 6c32c6b6676226e2e8af1dc9f43e5dcf9ed60eba Mon Sep 17 00:00:00 2001 From: Abel Milash Date: Tue, 19 May 2026 23:15:24 -0700 Subject: [PATCH 19/34] Align async fetchxml() docstring and comments with sync Add missing "Use for SQL-JOIN scenarios..." sentence, expand :return: description, add link-entity JOIN example, align Eager/Lazy comment wording, and add the two inline implementation comments from sync. Co-Authored-By: Claude Sonnet 4.6 --- .../Dataverse/aio/operations/async_query.py | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/PowerPlatform/Dataverse/aio/operations/async_query.py b/src/PowerPlatform/Dataverse/aio/operations/async_query.py index c2daa4e7..67fcb84f 100644 --- a/src/PowerPlatform/Dataverse/aio/operations/async_query.py +++ b/src/PowerPlatform/Dataverse/aio/operations/async_query.py @@ -159,10 +159,13 @@ def fetchxml(self, xml: str) -> AsyncFetchXmlQuery: :meth:`~PowerPlatform.Dataverse.models.async_fetchxml_query.AsyncFetchXmlQuery.execute_pages` is called on the returned object. + Use for SQL-JOIN scenarios, aggregate queries, or other operations that + the OData builder endpoint cannot express. + :param xml: Well-formed FetchXML query string. The root ```` element determines the entity set endpoint. :type xml: :class:`str` - :return: Inert async query object. + :return: Inert async query object with ``.execute()`` and ``.execute_pages()`` methods. :rtype: :class:`~PowerPlatform.Dataverse.models.async_fetchxml_query.AsyncFetchXmlQuery` :raises ValidationError: If the FetchXML is not a string, is empty, or exceeds the URL length limit when encoded. @@ -174,15 +177,19 @@ def fetchxml(self, xml: str) -> AsyncFetchXmlQuery: + + + \"\"\") - # Eager — all pages collected: + # Eager — collect all pages: result = await query.execute() df = result.to_dataframe() - # Lazy — one page at a time: + # Lazy — process one page at a time: async for page in query.execute_pages(): process(page.to_dataframe()) """ @@ -191,11 +198,19 @@ def fetchxml(self, xml: str) -> AsyncFetchXmlQuery: xml = xml.strip() if not xml: raise ValidationError("xml must not be empty") + # Fast-fail before any HTTP is attempted; execute_pages() re-checks the full URL + # (base + encoded XML) on each page. if len(_url_quote(xml, safe="")) > _MAX_URL_LENGTH: raise ValidationError( f"FetchXML exceeds the Dataverse URL length limit ({_MAX_URL_LENGTH:,} characters) when encoded. " "Use a $batch POST request to send FetchXML in the request body where the limit is 64 KB." ) + # Parse only to verify well-formedness and extract the entity name needed for the + # request URL. Structural and semantic validation is intentionally left to the server + # to avoid duplicating rules that may diverge from Dataverse's own enforcement. + # ElementTree does not resolve external entities or expand recursive internal entity + # references, so pathological inputs of that kind raise ParseError rather than + # consuming resources. try: root_el = _ET.fromstring(xml) except _ET.ParseError as exc: From e37fe4d91f1bfc0ed94c048a63d2869d52419037 Mon Sep 17 00:00:00 2001 From: Abel Milash Date: Wed, 20 May 2026 00:09:13 -0700 Subject: [PATCH 20/34] Fix async example scripts: remove deprecated method calls and close session on auth failure - functional_testing.py: replace batch.records.get() with batch.records.retrieve(); close client session on auth failure to avoid unclosed session warning - sql_examples.py: remove odata_select/odata_expand/odata_bind calls (deprecated sync-only helpers not available on async client); keep only odata_expands() Co-Authored-By: Claude Sonnet 4.6 --- examples/aio/advanced/sql_examples.py | 32 ++++-------------------- examples/aio/basic/functional_testing.py | 7 ++++-- 2 files changed, 10 insertions(+), 29 deletions(-) diff --git a/examples/aio/advanced/sql_examples.py b/examples/aio/advanced/sql_examples.py index 533dbaee..3848b6c3 100644 --- a/examples/aio/advanced/sql_examples.py +++ b/examples/aio/advanced/sql_examples.py @@ -25,7 +25,7 @@ - AND/OR, NOT IN, NOT LIKE boolean logic - Deep JOINs (5-8 tables) with no server depth limit - SQL helper functions: sql_columns (sql_select/sql_join/sql_joins removed at GA) -- OData helper functions: odata_select, odata_expands, odata_expand, odata_bind +- OData helper functions: odata_expands (odata_select/odata_expand/odata_bind are sync-only deprecated helpers) - SQL vs OData side-by-side comparison Not supported (server rejects): @@ -935,18 +935,13 @@ async def _run_examples(client): # ============================================================== # 30. OData Helper Functions # ============================================================== - heading(30, "OData Helper Functions (query.odata_* — deprecated at GA)") + heading(30, "OData Helper Functions (query.odata_expands)") print( - "odata_select(), odata_expand(), and odata_bind() still work at GA\n" - "but emit DeprecationWarning. Use the typed query builder instead.\n" - "odata_expands() is kept without deprecation." + "odata_expands() discovers navigation properties for $expand and @odata.bind.\n" + "odata_select(), odata_expand(), and odata_bind() are deprecated sync-only helpers\n" + "not available on the async client. Use sql_columns() and odata_expands() instead." ) - # odata_select - log_call(f"client.query.odata_select('{parent_table}')") - odata_cols = await client.query.odata_select(parent_table) - print(f"[OK] {len(odata_cols)} columns for $select: {odata_cols[:5]}...") - # odata_expands log_call(f"client.query.odata_expands('{child_table}')") try: @@ -957,23 +952,6 @@ async def _run_examples(client): except Exception as e: print(f"[WARN] {e}") - # odata_expand (single target) - try: - nav = await client.query.odata_expand(child_table, parent_table) - print(f"\n[OK] odata_expand('{child_table}', '{parent_table}') = '{nav}'") - print(" Usage: client.query.builder('" + child_table + "').expand('" + nav + "').execute()") - except Exception as e: - print(f"[WARN] {e}") - - # odata_bind - log_call("client.query.odata_bind(...)") - try: - bind = await client.query.odata_bind(child_table, parent_table, team_ids[0]) - print(f"[OK] {bind}") - print(" Merge into create/update payload: {{'new_Title': 'X', **bind}}") - except Exception as e: - print(f"[WARN] {e}") - # ============================================================== # 31. SQL vs OData Comparison # ============================================================== diff --git a/examples/aio/basic/functional_testing.py b/examples/aio/basic/functional_testing.py index a9b1f71d..3f5d940f 100644 --- a/examples/aio/basic/functional_testing.py +++ b/examples/aio/basic/functional_testing.py @@ -94,6 +94,7 @@ async def setup_authentication(): print("=" * 50) org_url = get_dataverse_org_url() + client = None try: credential = AsyncInteractiveBrowserCredential() client = AsyncDataverseClient(org_url, credential) @@ -111,6 +112,8 @@ async def setup_authentication(): except Exception as e: print(f"[ERR] Authentication failed: {e}") + if client is not None: + await client.aclose() sys.exit(1) @@ -790,12 +793,12 @@ async def test_batch_all_operations(client: AsyncDataverseClient, table_info: Di if all_ids: print(f"\n[10/11] Mixed batch (continue_on_error=True) — 1 bad get + 1 good get") batch = client.batch.new() - batch.records.get( + batch.records.retrieve( table_schema_name, "00000000-0000-0000-0000-000000000002", select=[f"{attr_prefix}_name"], ) - batch.records.get( + batch.records.retrieve( table_schema_name, all_ids[0], select=[f"{attr_prefix}_name"], From 5fba6de7eb0bc8eb9ba249e5fe9dd4f0f0d19c29 Mon Sep 17 00:00:00 2001 From: Abel Milash Date: Wed, 20 May 2026 09:18:58 -0700 Subject: [PATCH 21/34] Fix async file_upload example: use _AsyncResponse._body instead of .content _AsyncResponse buffers the body in ._body; it has no .content attribute (unlike requests.Response used by the sync client). Co-Authored-By: Claude Sonnet 4.6 --- examples/aio/advanced/file_upload.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/aio/advanced/file_upload.py b/examples/aio/advanced/file_upload.py index e44f6a7c..bbc13407 100644 --- a/examples/aio/advanced/file_upload.py +++ b/examples/aio/advanced/file_upload.py @@ -200,7 +200,7 @@ async def main(): async with client._scoped_odata() as od: dl_url = f"{od.api}/{entity_set}({record_id})/{small_file_attr_schema.lower()}/$value" resp = await od._request("get", dl_url) - content = await resp.read() if hasattr(resp, "read") else (resp.content or b"") + content = resp._body if hasattr(resp, "_body") else b"" downloaded_hash = hashlib.sha256(content).hexdigest() if content else None hash_match = (downloaded_hash == src_hash) if (downloaded_hash and src_hash) else None @@ -270,7 +270,7 @@ async def main(): async with client._scoped_odata() as od: dl_url = f"{od.api}/{entity_set}({record_id})/{chunk_file_attr_schema.lower()}/$value" resp = await od._request("get", dl_url) - content_chunk = await resp.read() if hasattr(resp, "read") else (resp.content or b"") + content_chunk = resp._body if hasattr(resp, "_body") else b"" dst_hash_chunk = hashlib.sha256(content_chunk).hexdigest() if content_chunk else None hash_match_chunk = ( From 1a9d5669774e2e8ecba297c74e15b5b116af85ed Mon Sep 17 00:00:00 2001 From: Abel Milash Date: Wed, 20 May 2026 10:03:06 -0700 Subject: [PATCH 22/34] Fix async file_upload example: fix remaining .content references for replace downloads Two download paths after replace uploads still used resp_r.content and resp_rc.content instead of ._body, causing AttributeError. Co-Authored-By: Claude Sonnet 4.6 --- examples/aio/advanced/file_upload.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/aio/advanced/file_upload.py b/examples/aio/advanced/file_upload.py index bbc13407..2b57a0b5 100644 --- a/examples/aio/advanced/file_upload.py +++ b/examples/aio/advanced/file_upload.py @@ -233,7 +233,7 @@ async def main(): async with client._scoped_odata() as od: dl_url = f"{od.api}/{entity_set}({record_id})/{small_file_attr_schema.lower()}/$value" resp_r = await od._request("get", dl_url) - content_r = await resp_r.read() if hasattr(resp_r, "read") else (resp_r.content or b"") + content_r = resp_r._body if hasattr(resp_r, "_body") else b"" dl_hash_r = hashlib.sha256(content_r).hexdigest() if content_r else None hash_match_r = (dl_hash_r == replace_hash) if (dl_hash_r and replace_hash) else None @@ -305,7 +305,7 @@ async def main(): async with client._scoped_odata() as od: dl_url = f"{od.api}/{entity_set}({record_id})/{chunk_file_attr_schema.lower()}/$value" resp_rc = await od._request("get", dl_url) - content_rc = await resp_rc.read() if hasattr(resp_rc, "read") else (resp_rc.content or b"") + content_rc = resp_rc._body if hasattr(resp_rc, "_body") else b"" dl_hash_rc = hashlib.sha256(content_rc).hexdigest() if content_rc else None hash_match_rc = (dl_hash_rc == replace_hash_c) if (dl_hash_rc and replace_hash_c) else None From 51e653f3e7bca447cd6a6e1f6ff0febdbf4068f1 Mon Sep 17 00:00:00 2001 From: Abel Milash Date: Wed, 20 May 2026 11:13:40 -0700 Subject: [PATCH 23/34] Add async concurrency benchmark and validation script MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Validates 7 properties of the async client against a live environment: 1. Non-blocking reads — canary confirms event loop stays free during GETs 2. Read throughput — concurrent reads via gather() beat sequential 3. Write concurrency — concurrent POSTs beat sequential (POST path) 4. Pagination non-blocking — async generators yield between page fetches 5. Mixed fan-out — different op types run simultaneously without serialization 6. Error resilience — one failure in gather() does not kill other calls 7. Real-world fan-out — metadata for multiple tables fetched in parallel Co-Authored-By: Claude Sonnet 4.6 --- .../aio/advanced/concurrency_benchmark.py | 602 ++++++++++++++++++ 1 file changed, 602 insertions(+) create mode 100644 examples/aio/advanced/concurrency_benchmark.py diff --git a/examples/aio/advanced/concurrency_benchmark.py b/examples/aio/advanced/concurrency_benchmark.py new file mode 100644 index 00000000..1e711db3 --- /dev/null +++ b/examples/aio/advanced/concurrency_benchmark.py @@ -0,0 +1,602 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +""" +Async concurrency benchmark and validation for the Dataverse Python SDK. + +Validates seven properties of the async client end-to-end: + + 1. Non-blocking reads — GET calls do not freeze the event loop. + 2. Read throughput — N reads via asyncio.gather() beat sequential. + 3. Write concurrency — N parallel creates beat sequential; POST path. + 4. Pagination non-blocking— async generator yields between pages (canary + keeps ticking between page fetches). + 5. Mixed fan-out — different operation types run simultaneously. + 6. Error resilience — one failing call in gather() does not kill + the others (return_exceptions=True pattern). + 7. Real-world fan-out — metadata for multiple tables in parallel. + +How to interpret results +------------------------ +- Max tick gap (canary tests): Windows timer resolution is ~15 ms, so gaps + up to ~30 ms are normal. Gaps > 200 ms indicate a blocking call. +- Speedup: depends on network latency. Expect 3-15x on WAN. Very low + speedup (<2x) suggests server throttling or accidental serialization. + +Tip: run with PYTHONASYNCIODEBUG=1 for the asyncio debug mode which logs +a warning whenever a coroutine holds the event loop for more than 100 ms. + +Requirements: + pip install PowerPlatform-Dataverse-Client[async] azure-identity +""" + +from __future__ import annotations + +import asyncio +import sys +import time +import uuid +from pathlib import Path +from typing import Any, Callable, List + +sys.path.insert(0, str(Path(__file__).resolve().parents[1])) +from _auth import AsyncInteractiveBrowserCredential +from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient +from PowerPlatform.Dataverse.models.record import QueryResult +from PowerPlatform.Dataverse.models.table_info import TableInfo + + +# --------------------------------------------------------------------------- +# Shared helpers +# --------------------------------------------------------------------------- + +SEPARATOR = "=" * 72 +_WARN_GAP_MS = 200.0 # max acceptable canary gap in milliseconds +_WARN_SPEEDUP = 2.0 # min acceptable speedup ratio + + +def heading(title: str) -> None: + print(f"\n{SEPARATOR}") + print(title) + print(SEPARATOR) + + +def _speedup_line(label: str, seq_s: float, conc_s: float) -> str: + speedup = seq_s / conc_s if conc_s > 0 else float("inf") + status = "[OK] " if speedup >= _WARN_SPEEDUP else "[WARN]" + return ( + f" {status} {label}\n" + f" sequential={seq_s:.2f}s concurrent={conc_s:.2f}s " + f"speedup={speedup:.1f}x" + ), speedup + + +# --------------------------------------------------------------------------- +# Canary infrastructure +# --------------------------------------------------------------------------- + +async def _canary(ticks: List[float], stop: asyncio.Event) -> None: + """Append monotonic timestamps every 10 ms until *stop* is set.""" + while not stop.is_set(): + ticks.append(time.monotonic()) + await asyncio.sleep(0.01) + + +def _max_gap_ms(ticks: List[float]) -> float: + if len(ticks) < 2: + return 0.0 + return max(ticks[i + 1] - ticks[i] for i in range(len(ticks) - 1)) * 1000 + + +async def _with_canary(coro_fn: Callable) -> tuple[Any, float, int, float]: + """ + Run *coro_fn()* while a canary ticks every 10 ms. + + Returns (result, elapsed_ms, tick_count, max_gap_ms). + """ + ticks: List[float] = [] + stop = asyncio.Event() + task = asyncio.create_task(_canary(ticks, stop)) + t0 = time.monotonic() + result = await coro_fn() + elapsed_ms = (time.monotonic() - t0) * 1000 + stop.set() + await task + return result, elapsed_ms, len(ticks), _max_gap_ms(ticks) + + +def _canary_line(label: str, elapsed_ms: float, ticks: int, gap_ms: float) -> tuple[str, bool]: + ok = gap_ms < _WARN_GAP_MS + status = "[OK] " if ok else "[WARN]" + line = ( + f" {status} {label}\n" + f" call={elapsed_ms:.0f}ms canary_ticks={ticks} max_gap={gap_ms:.1f}ms" + ) + return line, ok + + +# --------------------------------------------------------------------------- +# Test 1: Non-blocking reads (GET operations) +# --------------------------------------------------------------------------- + +async def run_test1_nonblocking_reads(client: AsyncDataverseClient) -> None: + """ + Verify that read operations (GET) do not block the event loop. + + Covers: records.list, tables.list, query.sql, query.fetchxml, + query.builder, tables.get. + """ + heading("Test 1: Non-Blocking Reads (GET canary)") + print( + "A canary ticks every 10 ms during each read call.\n" + "Max gap should stay near 10-30 ms (Windows timer resolution).\n" + "Gaps > 200 ms indicate a blocking call in the async path.\n" + ) + + fetchxml = """ + + + + + + """ + + calls = [ + ("records.list(account, top=5)", + lambda: client.records.list("account", top=5)), + ("tables.list(filter=IsPrivate...)", + lambda: client.tables.list( + filter="IsPrivate eq false", + select=["LogicalName", "SchemaName"], + )), + ("tables.get(account)", + lambda: client.tables.get("account")), + ("query.sql(SELECT TOP 5 ...)", + lambda: client.query.sql("SELECT TOP 5 name FROM account ORDER BY name")), + ("query.fetchxml(...).execute()", + lambda: client.query.fetchxml(fetchxml).execute()), + ("query.builder(account).top(5).execute()", + lambda: client.query.builder("account").select("name").top(5).execute()), + ] + + all_ok = True + for label, coro_fn in calls: + _, elapsed_ms, ticks, gap_ms = await _with_canary(coro_fn) + line, ok = _canary_line(label, elapsed_ms, ticks, gap_ms) + print(line) + if not ok: + all_ok = False + + print() + if all_ok: + print("[OK] Event loop stayed unblocked across all read operations.") + else: + print( + "[WARN] One or more calls produced a large tick gap.\n" + " Check for time.sleep, sync HTTP calls, or blocking file I/O." + ) + + +# --------------------------------------------------------------------------- +# Test 2: Read throughput — sequential vs concurrent +# --------------------------------------------------------------------------- + +async def run_test2_read_throughput(client: AsyncDataverseClient, n: int) -> None: + """ + Compare sequential vs concurrent execution for read operations. + + Expected speedup: 3-15x depending on network latency and server throttling. + """ + heading(f"Test 2: Read Throughput — Sequential vs Concurrent (N={n})") + print( + f"Each read operation is called {n} times sequentially, then\n" + f"all {n} at once via asyncio.gather().\n" + f"Expected: ≥{_WARN_SPEEDUP:.0f}x speedup (typically 3-15x on WAN).\n" + ) + + ops = [ + ("records.list(account, top=5)", + lambda: client.records.list("account", top=5)), + ("query.sql(SELECT TOP 5 ...)", + lambda: client.query.sql("SELECT TOP 5 name FROM account ORDER BY name")), + ("tables.get(account)", + lambda: client.tables.get("account")), + ] + + overall_seq = overall_conc = 0.0 + for label, coro_fn in ops: + t0 = time.monotonic() + for _ in range(n): + await coro_fn() + seq_s = time.monotonic() - t0 + + t0 = time.monotonic() + await asyncio.gather(*[coro_fn() for _ in range(n)]) + conc_s = time.monotonic() - t0 + + line, speedup = _speedup_line(label, seq_s, conc_s) + print(line) + overall_seq += seq_s + overall_conc += conc_s + + overall_speedup = overall_seq / overall_conc if overall_conc > 0 else float("inf") + print(f"\n Overall speedup: {overall_speedup:.1f}x") + if overall_speedup >= _WARN_SPEEDUP: + print("[OK] Read concurrency confirmed.") + else: + print( + "[WARN] Low speedup. Possible causes: low latency environment,\n" + " server throttling, or accidental serialization." + ) + + +# --------------------------------------------------------------------------- +# Test 3: Write concurrency — POST path +# --------------------------------------------------------------------------- + +async def run_test3_write_concurrency(client: AsyncDataverseClient, n: int) -> None: + """ + Verify that write operations (POST) also benefit from concurrency. + + Creates N records sequentially then N records in parallel, compares + elapsed time, and deletes all created records. + + The POST path has a 120 s default timeout (vs 10 s for GET), so it + exercises a different code branch in _AsyncHttpClient. + """ + heading(f"Test 3: Write Concurrency — Sequential vs Concurrent (N={n})") + print( + f"Creates {n} contact records sequentially, then {n} concurrently.\n" + f"All records are deleted at the end of this test.\n" + f"Tests the POST path (different timeout branch from GET).\n" + ) + + tag = uuid.uuid4().hex[:8] + + def _payload(i: int, suffix: str) -> dict: + return { + "firstname": f"Bench{suffix}", + "lastname": f"{tag}-{i}", + } + + # Sequential creates + seq_ids: List[str] = [] + t0 = time.monotonic() + for i in range(n): + rid = await client.records.create("contact", _payload(i, "Seq")) + seq_ids.append(rid) + seq_s = time.monotonic() - t0 + + # Concurrent creates + t0 = time.monotonic() + conc_ids = list(await asyncio.gather( + *[client.records.create("contact", _payload(i, "Con")) for i in range(n)] + )) + conc_s = time.monotonic() - t0 + + line, speedup = _speedup_line(f"records.create(contact) x{n}", seq_s, conc_s) + print(line) + + # Cleanup — delete all created records concurrently + all_ids = seq_ids + [rid for rid in conc_ids if rid] + if all_ids: + await client.records.delete("contact", all_ids) + print(f"\n [OK] Cleaned up {len(all_ids)} test records.") + + print() + if speedup >= _WARN_SPEEDUP: + print("[OK] Write concurrency (POST path) confirmed.") + else: + print("[WARN] Low write speedup — may indicate server throttling on contact creates.") + + +# --------------------------------------------------------------------------- +# Test 4: Pagination non-blocking +# --------------------------------------------------------------------------- + +async def run_test4_pagination_nonblocking(client: AsyncDataverseClient) -> None: + """ + Verify that async generators (list_pages, execute_pages) yield between + page fetches, keeping the event loop free. + + The canary runs for the entire multi-page iteration. Between each page + fetch the event loop is idle and the canary should tick. If pagination + were implemented with blocking I/O or time.sleep, the canary would + stop ticking between pages. + """ + heading("Test 4: Pagination Non-Blocking (async generator canary)") + print( + "Runs a multi-page query while the canary ticks.\n" + "The event loop should stay free between page fetches.\n" + "Canary ticks between pages confirm yields at page boundaries.\n" + ) + + fetchxml = """ + + + + + + + """ + + async def _paginate_records(): + pages = 0 + async for _page in client.records.list_pages("account", top=5, page_size=5): + pages += 1 + if pages >= 3: + break + + async def _paginate_fetchxml(): + pages = 0 + async for _page in client.query.fetchxml(fetchxml).execute_pages(): + pages += 1 + if pages >= 3: + break + + async def _paginate_builder(): + pages = 0 + async for _page in ( + client.query.builder("account") + .select("name") + .page_size(5) + .execute_pages() + ): + pages += 1 + if pages >= 3: + break + + paginators = [ + ("records.list_pages(account, page_size=5)", _paginate_records), + ("query.fetchxml(...).execute_pages()", _paginate_fetchxml), + ("query.builder(...).execute_pages()", _paginate_builder), + ] + + all_ok = True + for label, coro_fn in paginators: + _, elapsed_ms, ticks, gap_ms = await _with_canary(coro_fn) + line, ok = _canary_line(label, elapsed_ms, ticks, gap_ms) + print(line) + if not ok: + all_ok = False + + print() + if all_ok: + print("[OK] Async generators yield between pages — event loop stays free.") + else: + print("[WARN] Large gap detected during pagination — possible blocking between pages.") + + +# --------------------------------------------------------------------------- +# Test 5: Mixed fan-out (different operation types simultaneously) +# --------------------------------------------------------------------------- + +async def run_test5_mixed_fanout(client: AsyncDataverseClient) -> None: + """ + Fire different operation types concurrently in a single gather(). + + Real applications mix reads, metadata queries, and SQL in parallel. + This test confirms there is no cross-operation serialization — + e.g. an internal lock that would cause records.list to wait for + tables.get to finish before starting. + """ + heading("Test 5: Mixed Fan-Out (different operations simultaneously)") + print( + "Fires records.list, tables.get, query.sql, query.fetchxml, and\n" + "query.builder all at once in a single asyncio.gather().\n" + "Verifies no cross-operation serialization exists in the SDK.\n" + ) + + fetchxml = """ + + + + + + """ + + ops = { + "records.list(account, top=3)": lambda: client.records.list("account", top=3), + "tables.get(account)": lambda: client.tables.get("account"), + "tables.get(contact)": lambda: client.tables.get("contact"), + "query.sql(SELECT TOP 3 ...)": lambda: client.query.sql( + "SELECT TOP 3 name FROM account ORDER BY name" + ), + "query.fetchxml(...).execute()": lambda: client.query.fetchxml(fetchxml).execute(), + "query.builder(account).top(3).execute()": lambda: ( + client.query.builder("account").select("name").top(3).execute() + ), + } + + # Sequential baseline + t0 = time.monotonic() + for coro_fn in ops.values(): + await coro_fn() + seq_s = time.monotonic() - t0 + + # All at once + t0 = time.monotonic() + results = await asyncio.gather(*[fn() for fn in ops.values()]) + conc_s = time.monotonic() - t0 + + speedup = seq_s / conc_s if conc_s > 0 else float("inf") + status = "[OK] " if speedup >= _WARN_SPEEDUP else "[WARN]" + + print(f" {status} Mixed {len(ops)}-operation fan-out") + print(f" sequential={seq_s:.2f}s concurrent={conc_s:.2f}s speedup={speedup:.1f}x\n") + + for label, result in zip(ops.keys(), results): + if isinstance(result, QueryResult): + print(f" [OK] {label} → {len(result.records)} result(s)") + elif isinstance(result, TableInfo): + print(f" [OK] {label} → {result.schema_name}") + elif isinstance(result, list): + print(f" [OK] {label} → {len(result)} result(s)") + elif result is None: + print(f" [OK] {label} → None") + else: + print(f" [OK] {label} → {type(result).__name__}") + + print() + if speedup >= _WARN_SPEEDUP: + print("[OK] Mixed fan-out confirmed — no cross-operation serialization.") + else: + print("[WARN] Low mixed fan-out speedup.") + + +# --------------------------------------------------------------------------- +# Test 6: Error resilience in gather() +# --------------------------------------------------------------------------- + +async def run_test6_error_resilience(client: AsyncDataverseClient) -> None: + """ + Verify that one failing call in asyncio.gather() does not prevent + the others from completing when return_exceptions=True is used. + + This is an important usage pattern: in a batch of N concurrent calls, + a single 404 or throttle error should not discard the N-1 successful + results. This test demonstrates and validates the correct pattern. + """ + heading("Test 6: Error Resilience in gather(return_exceptions=True)") + print( + "Fires 5 calls: 3 valid records.list, 1 intentionally bad SQL,\n" + "and 1 list against a nonexistent table.\n" + "With return_exceptions=True the 3 good calls must complete\n" + "and return results even though 2 calls fail.\n" + ) + + bad_sql = "SELECT name INVALID SYNTAX FROM account" + nonexistent_table = "new_TableThatDefinitelyDoesNotExist_xyz987" + + coros = [ + client.records.list("account", top=3), # good + client.records.list("contact", top=3), # good + client.query.sql("SELECT TOP 3 name FROM account ORDER BY name"), # good + client.query.sql(bad_sql), # bad — invalid SQL + client.records.list(nonexistent_table, top=1), # bad — table not found + ] + + t0 = time.monotonic() + results = await asyncio.gather(*coros, return_exceptions=True) + elapsed_ms = (time.monotonic() - t0) * 1000 + + succeeded = [r for r in results if not isinstance(r, BaseException)] + failed = [r for r in results if isinstance(r, BaseException)] + + print(f" Elapsed: {elapsed_ms:.0f}ms") + print(f" Succeeded: {len(succeeded)} / {len(results)}") + print(f" Failed: {len(failed)} / {len(results)}\n") + + for i, r in enumerate(results): + if isinstance(r, BaseException): + print(f" [ERR] Call {i+1}: {type(r).__name__}: {str(r)[:80]}") + else: + count = len(r) if hasattr(r, "__len__") else ("None" if r is None else "1") + print(f" [OK] Call {i+1}: {count} result(s)") + + print() + if len(succeeded) == 3 and len(failed) == 2: + print("[OK] Error resilience confirmed — good calls completed despite failures.") + else: + print(f"[WARN] Expected 3 successes and 2 failures, got {len(succeeded)}/{len(failed)}.") + + +# --------------------------------------------------------------------------- +# Test 7: Real-world fan-out — metadata for multiple tables +# --------------------------------------------------------------------------- + +async def run_test7_metadata_fanout(client: AsyncDataverseClient) -> None: + """ + Fetch metadata for multiple tables simultaneously. + + The canonical real-world use case: an application needs schema info + for several tables at startup. Sequential is simple but slow; + concurrent fan-out is fast and equally readable with gather(). + """ + heading("Test 7: Real-World Metadata Fan-Out") + print( + "Fetch metadata for 6 built-in tables: first sequentially,\n" + "then all at once. This is the canonical real-world async pattern.\n" + ) + + tables = ["account", "contact", "lead", "opportunity", "systemuser", "task"] + + t0 = time.monotonic() + for t in tables: + await client.tables.get(t) + seq_s = time.monotonic() - t0 + + t0 = time.monotonic() + results = await asyncio.gather(*[client.tables.get(t) for t in tables]) + conc_s = time.monotonic() - t0 + + speedup = seq_s / conc_s if conc_s > 0 else float("inf") + status = "[OK] " if speedup >= _WARN_SPEEDUP else "[WARN]" + + print(f" {status} tables.get() x{len(tables)}") + print(f" sequential={seq_s:.2f}s concurrent={conc_s:.2f}s speedup={speedup:.1f}x\n") + + for info in results: + if info: + print(f" [OK] {info.get('schema_name', '?'):20s} entity_set={info.get('entity_set_name', '?')}") + + print() + if speedup >= _WARN_SPEEDUP: + print(f"[OK] Metadata fan-out speedup: {speedup:.1f}x") + else: + print(f"[WARN] Low metadata fan-out speedup: {speedup:.1f}x") + + +# --------------------------------------------------------------------------- +# Entry point +# --------------------------------------------------------------------------- + +async def main() -> None: + print(SEPARATOR) + print("Dataverse SDK - Async Concurrency Benchmark & Validation") + print(SEPARATOR) + print( + "\nValidates 7 properties of the async client:\n" + " 1. Non-blocking reads — GET calls yield to event loop\n" + " 2. Read throughput — concurrent reads beat sequential\n" + " 3. Write concurrency — concurrent POSTs beat sequential\n" + " 4. Pagination non-blocking — async generators yield between pages\n" + " 5. Mixed fan-out — different op types run simultaneously\n" + " 6. Error resilience — one failure doesn't kill other calls\n" + " 7. Real-world fan-out — metadata for multiple tables in parallel\n" + "\nTip: run with PYTHONASYNCIODEBUG=1 to catch any remaining blocking calls.\n" + ) + + base_url = input("Enter Dataverse org URL (e.g. https://yourorg.crm.dynamics.com): ").strip() + if not base_url: + print("No URL entered; exiting.") + sys.exit(1) + base_url = base_url.rstrip("/") + + n_str = input("Calls per operation for throughput tests [default: 8]: ").strip() + n = int(n_str) if n_str.isdigit() and int(n_str) > 0 else 8 + + credential = AsyncInteractiveBrowserCredential() + try: + async with AsyncDataverseClient(base_url=base_url, credential=credential) as client: + print("\nWarming up (first call triggers auth + connection)...") + await client.records.list("account", top=1) + print("[OK] Warm-up complete.") + + await run_test1_nonblocking_reads(client) + await run_test2_read_throughput(client, n=n) + await run_test3_write_concurrency(client, n=n) + await run_test4_pagination_nonblocking(client) + await run_test5_mixed_fanout(client) + await run_test6_error_resilience(client) + await run_test7_metadata_fanout(client) + + print(f"\n{SEPARATOR}") + print("Benchmark complete.") + print(SEPARATOR) + finally: + await credential.close() + + +if __name__ == "__main__": + asyncio.run(main()) From d7310be364b12c046080e7a1dfbdb483014fa375 Mon Sep 17 00:00:00 2001 From: Abel Milash Date: Wed, 20 May 2026 11:26:33 -0700 Subject: [PATCH 24/34] Expand concurrency_benchmark.py docstring with per-test descriptions Each test now has a brief explanation of what it runs, what property it validates, and what a failure would indicate. Also clarifies that speedup measures async-sequential vs async-concurrent, not async vs sync. Co-Authored-By: Claude Sonnet 4.6 --- .../aio/advanced/concurrency_benchmark.py | 68 +++++++++++++++---- 1 file changed, 53 insertions(+), 15 deletions(-) diff --git a/examples/aio/advanced/concurrency_benchmark.py b/examples/aio/advanced/concurrency_benchmark.py index 1e711db3..251fe830 100644 --- a/examples/aio/advanced/concurrency_benchmark.py +++ b/examples/aio/advanced/concurrency_benchmark.py @@ -4,27 +4,65 @@ """ Async concurrency benchmark and validation for the Dataverse Python SDK. -Validates seven properties of the async client end-to-end: - - 1. Non-blocking reads — GET calls do not freeze the event loop. - 2. Read throughput — N reads via asyncio.gather() beat sequential. - 3. Write concurrency — N parallel creates beat sequential; POST path. - 4. Pagination non-blocking— async generator yields between pages (canary - keeps ticking between page fetches). - 5. Mixed fan-out — different operation types run simultaneously. - 6. Error resilience — one failing call in gather() does not kill - the others (return_exceptions=True pattern). - 7. Real-world fan-out — metadata for multiple tables in parallel. +Measures async-sequential vs async-concurrent performance (not async vs sync). +Speedup = time for N sequential awaits / time for N concurrent gather() calls. + +Tests +----- +1. Non-blocking reads (canary) + A background task ticks every 10 ms while each GET call runs. Measures the + max gap between ticks. A blocking call (e.g. requests instead of aiohttp, + or time.sleep instead of asyncio.sleep) would starve the canary and produce + a gap equal to the full round-trip. Covers: records.list, tables.list, + tables.get, query.sql, query.fetchxml, query.builder. + +2. Read throughput (sequential vs concurrent) + Runs N reads sequentially then N reads with asyncio.gather(). Confirms the + HTTP GET path actually parallelizes. An internal lock or misplaced await + would collapse the speedup to ~1x. Covers: records.list, query.sql, + tables.get. + +3. Write concurrency (POST path) + Same as Test 2 but for records.create() (POST). The POST path uses a + different timeout branch (120 s vs 10 s for GET) and different server + behavior. A separate test ensures writes are also truly concurrent, not + just reads. Creates N records in parallel then cleans them up. + +4. Pagination non-blocking (async generator canary) + Runs list_pages(), fetchxml().execute_pages(), and builder().execute_pages() + while the canary ticks. Verifies the async generator yields control back to + the event loop between page fetches. A generator that does not await + properly between pages would block other tasks during multi-page queries. + +5. Mixed fan-out (cross-operation concurrency) + Fires 6 different operation types simultaneously in one gather(): records.list, + tables.get (x2), query.sql, query.fetchxml, query.builder. A shared internal + resource (metadata cache lock, single connection) could accidentally serialize + different operation types even if same-type parallelism works fine. This test + catches cross-operation serialization. + +6. Error resilience + Fires 5 calls — 3 good, 2 intentionally bad — using gather(return_exceptions=True). + Verifies the 3 good calls complete and return results despite the 2 failures. + Without return_exceptions=True, one exception cancels all in-flight coroutines. + Validates the correct usage pattern and confirms the SDK does not suppress + exceptions in a way that would break this pattern. + +7. Real-world metadata fan-out + Fetches schema info for 6 tables sequentially then in parallel. The most + common real-world async use case: an application needs metadata for several + tables at startup. Demonstrates the pattern works end-to-end with real results. How to interpret results ------------------------ +- Speedup: async-sequential vs async-concurrent, not async vs sync. + Expect 3-15x on WAN. Low speedup (<2x) suggests server throttling + or accidental serialization in the SDK. - Max tick gap (canary tests): Windows timer resolution is ~15 ms, so gaps up to ~30 ms are normal. Gaps > 200 ms indicate a blocking call. -- Speedup: depends on network latency. Expect 3-15x on WAN. Very low - speedup (<2x) suggests server throttling or accidental serialization. -Tip: run with PYTHONASYNCIODEBUG=1 for the asyncio debug mode which logs -a warning whenever a coroutine holds the event loop for more than 100 ms. +Tip: run with PYTHONASYNCIODEBUG=1 to log a warning whenever a coroutine +holds the event loop for more than 100 ms. Requirements: pip install PowerPlatform-Dataverse-Client[async] azure-identity From cb0d711baab57fe9c2c94b99ca2d352d11302d22 Mon Sep 17 00:00:00 2001 From: Abel Milash Date: Wed, 20 May 2026 11:30:23 -0700 Subject: [PATCH 25/34] Apply black formatting to concurrency_benchmark.py Co-Authored-By: Claude Sonnet 4.6 --- .../aio/advanced/concurrency_benchmark.py | 93 +++++++++---------- 1 file changed, 43 insertions(+), 50 deletions(-) diff --git a/examples/aio/advanced/concurrency_benchmark.py b/examples/aio/advanced/concurrency_benchmark.py index 251fe830..d537d13f 100644 --- a/examples/aio/advanced/concurrency_benchmark.py +++ b/examples/aio/advanced/concurrency_benchmark.py @@ -83,14 +83,13 @@ from PowerPlatform.Dataverse.models.record import QueryResult from PowerPlatform.Dataverse.models.table_info import TableInfo - # --------------------------------------------------------------------------- # Shared helpers # --------------------------------------------------------------------------- SEPARATOR = "=" * 72 _WARN_GAP_MS = 200.0 # max acceptable canary gap in milliseconds -_WARN_SPEEDUP = 2.0 # min acceptable speedup ratio +_WARN_SPEEDUP = 2.0 # min acceptable speedup ratio def heading(title: str) -> None: @@ -113,6 +112,7 @@ def _speedup_line(label: str, seq_s: float, conc_s: float) -> str: # Canary infrastructure # --------------------------------------------------------------------------- + async def _canary(ticks: List[float], stop: asyncio.Event) -> None: """Append monotonic timestamps every 10 ms until *stop* is set.""" while not stop.is_set(): @@ -146,10 +146,7 @@ async def _with_canary(coro_fn: Callable) -> tuple[Any, float, int, float]: def _canary_line(label: str, elapsed_ms: float, ticks: int, gap_ms: float) -> tuple[str, bool]: ok = gap_ms < _WARN_GAP_MS status = "[OK] " if ok else "[WARN]" - line = ( - f" {status} {label}\n" - f" call={elapsed_ms:.0f}ms canary_ticks={ticks} max_gap={gap_ms:.1f}ms" - ) + line = f" {status} {label}\n" f" call={elapsed_ms:.0f}ms canary_ticks={ticks} max_gap={gap_ms:.1f}ms" return line, ok @@ -157,6 +154,7 @@ def _canary_line(label: str, elapsed_ms: float, ticks: int, gap_ms: float) -> tu # Test 1: Non-blocking reads (GET operations) # --------------------------------------------------------------------------- + async def run_test1_nonblocking_reads(client: AsyncDataverseClient) -> None: """ Verify that read operations (GET) do not block the event loop. @@ -180,21 +178,21 @@ async def run_test1_nonblocking_reads(client: AsyncDataverseClient) -> None: """ calls = [ - ("records.list(account, top=5)", - lambda: client.records.list("account", top=5)), - ("tables.list(filter=IsPrivate...)", - lambda: client.tables.list( - filter="IsPrivate eq false", - select=["LogicalName", "SchemaName"], - )), - ("tables.get(account)", - lambda: client.tables.get("account")), - ("query.sql(SELECT TOP 5 ...)", - lambda: client.query.sql("SELECT TOP 5 name FROM account ORDER BY name")), - ("query.fetchxml(...).execute()", - lambda: client.query.fetchxml(fetchxml).execute()), - ("query.builder(account).top(5).execute()", - lambda: client.query.builder("account").select("name").top(5).execute()), + ("records.list(account, top=5)", lambda: client.records.list("account", top=5)), + ( + "tables.list(filter=IsPrivate...)", + lambda: client.tables.list( + filter="IsPrivate eq false", + select=["LogicalName", "SchemaName"], + ), + ), + ("tables.get(account)", lambda: client.tables.get("account")), + ("query.sql(SELECT TOP 5 ...)", lambda: client.query.sql("SELECT TOP 5 name FROM account ORDER BY name")), + ("query.fetchxml(...).execute()", lambda: client.query.fetchxml(fetchxml).execute()), + ( + "query.builder(account).top(5).execute()", + lambda: client.query.builder("account").select("name").top(5).execute(), + ), ] all_ok = True @@ -219,6 +217,7 @@ async def run_test1_nonblocking_reads(client: AsyncDataverseClient) -> None: # Test 2: Read throughput — sequential vs concurrent # --------------------------------------------------------------------------- + async def run_test2_read_throughput(client: AsyncDataverseClient, n: int) -> None: """ Compare sequential vs concurrent execution for read operations. @@ -233,12 +232,9 @@ async def run_test2_read_throughput(client: AsyncDataverseClient, n: int) -> Non ) ops = [ - ("records.list(account, top=5)", - lambda: client.records.list("account", top=5)), - ("query.sql(SELECT TOP 5 ...)", - lambda: client.query.sql("SELECT TOP 5 name FROM account ORDER BY name")), - ("tables.get(account)", - lambda: client.tables.get("account")), + ("records.list(account, top=5)", lambda: client.records.list("account", top=5)), + ("query.sql(SELECT TOP 5 ...)", lambda: client.query.sql("SELECT TOP 5 name FROM account ORDER BY name")), + ("tables.get(account)", lambda: client.tables.get("account")), ] overall_seq = overall_conc = 0.0 @@ -272,6 +268,7 @@ async def run_test2_read_throughput(client: AsyncDataverseClient, n: int) -> Non # Test 3: Write concurrency — POST path # --------------------------------------------------------------------------- + async def run_test3_write_concurrency(client: AsyncDataverseClient, n: int) -> None: """ Verify that write operations (POST) also benefit from concurrency. @@ -307,9 +304,7 @@ def _payload(i: int, suffix: str) -> dict: # Concurrent creates t0 = time.monotonic() - conc_ids = list(await asyncio.gather( - *[client.records.create("contact", _payload(i, "Con")) for i in range(n)] - )) + conc_ids = list(await asyncio.gather(*[client.records.create("contact", _payload(i, "Con")) for i in range(n)])) conc_s = time.monotonic() - t0 line, speedup = _speedup_line(f"records.create(contact) x{n}", seq_s, conc_s) @@ -332,6 +327,7 @@ def _payload(i: int, suffix: str) -> dict: # Test 4: Pagination non-blocking # --------------------------------------------------------------------------- + async def run_test4_pagination_nonblocking(client: AsyncDataverseClient) -> None: """ Verify that async generators (list_pages, execute_pages) yield between @@ -374,20 +370,15 @@ async def _paginate_fetchxml(): async def _paginate_builder(): pages = 0 - async for _page in ( - client.query.builder("account") - .select("name") - .page_size(5) - .execute_pages() - ): + async for _page in client.query.builder("account").select("name").page_size(5).execute_pages(): pages += 1 if pages >= 3: break paginators = [ ("records.list_pages(account, page_size=5)", _paginate_records), - ("query.fetchxml(...).execute_pages()", _paginate_fetchxml), - ("query.builder(...).execute_pages()", _paginate_builder), + ("query.fetchxml(...).execute_pages()", _paginate_fetchxml), + ("query.builder(...).execute_pages()", _paginate_builder), ] all_ok = True @@ -409,6 +400,7 @@ async def _paginate_builder(): # Test 5: Mixed fan-out (different operation types simultaneously) # --------------------------------------------------------------------------- + async def run_test5_mixed_fanout(client: AsyncDataverseClient) -> None: """ Fire different operation types concurrently in a single gather(). @@ -434,13 +426,11 @@ async def run_test5_mixed_fanout(client: AsyncDataverseClient) -> None: """ ops = { - "records.list(account, top=3)": lambda: client.records.list("account", top=3), - "tables.get(account)": lambda: client.tables.get("account"), - "tables.get(contact)": lambda: client.tables.get("contact"), - "query.sql(SELECT TOP 3 ...)": lambda: client.query.sql( - "SELECT TOP 3 name FROM account ORDER BY name" - ), - "query.fetchxml(...).execute()": lambda: client.query.fetchxml(fetchxml).execute(), + "records.list(account, top=3)": lambda: client.records.list("account", top=3), + "tables.get(account)": lambda: client.tables.get("account"), + "tables.get(contact)": lambda: client.tables.get("contact"), + "query.sql(SELECT TOP 3 ...)": lambda: client.query.sql("SELECT TOP 3 name FROM account ORDER BY name"), + "query.fetchxml(...).execute()": lambda: client.query.fetchxml(fetchxml).execute(), "query.builder(account).top(3).execute()": lambda: ( client.query.builder("account").select("name").top(3).execute() ), @@ -486,6 +476,7 @@ async def run_test5_mixed_fanout(client: AsyncDataverseClient) -> None: # Test 6: Error resilience in gather() # --------------------------------------------------------------------------- + async def run_test6_error_resilience(client: AsyncDataverseClient) -> None: """ Verify that one failing call in asyncio.gather() does not prevent @@ -507,11 +498,11 @@ async def run_test6_error_resilience(client: AsyncDataverseClient) -> None: nonexistent_table = "new_TableThatDefinitelyDoesNotExist_xyz987" coros = [ - client.records.list("account", top=3), # good - client.records.list("contact", top=3), # good - client.query.sql("SELECT TOP 3 name FROM account ORDER BY name"), # good - client.query.sql(bad_sql), # bad — invalid SQL - client.records.list(nonexistent_table, top=1), # bad — table not found + client.records.list("account", top=3), # good + client.records.list("contact", top=3), # good + client.query.sql("SELECT TOP 3 name FROM account ORDER BY name"), # good + client.query.sql(bad_sql), # bad — invalid SQL + client.records.list(nonexistent_table, top=1), # bad — table not found ] t0 = time.monotonic() @@ -543,6 +534,7 @@ async def run_test6_error_resilience(client: AsyncDataverseClient) -> None: # Test 7: Real-world fan-out — metadata for multiple tables # --------------------------------------------------------------------------- + async def run_test7_metadata_fanout(client: AsyncDataverseClient) -> None: """ Fetch metadata for multiple tables simultaneously. @@ -589,6 +581,7 @@ async def run_test7_metadata_fanout(client: AsyncDataverseClient) -> None: # Entry point # --------------------------------------------------------------------------- + async def main() -> None: print(SEPARATOR) print("Dataverse SDK - Async Concurrency Benchmark & Validation") From da41fcc2a9d090b2804dfc301d7de0941ece3049 Mon Sep 17 00:00:00 2001 From: Abel Milash Date: Wed, 20 May 2026 11:31:39 -0700 Subject: [PATCH 26/34] Shorten per-test descriptions in concurrency_benchmark.py docstring Co-Authored-By: Claude Sonnet 4.6 --- .../aio/advanced/concurrency_benchmark.py | 57 +++++-------------- 1 file changed, 14 insertions(+), 43 deletions(-) diff --git a/examples/aio/advanced/concurrency_benchmark.py b/examples/aio/advanced/concurrency_benchmark.py index d537d13f..bb06f256 100644 --- a/examples/aio/advanced/concurrency_benchmark.py +++ b/examples/aio/advanced/concurrency_benchmark.py @@ -9,49 +9,20 @@ Tests ----- -1. Non-blocking reads (canary) - A background task ticks every 10 ms while each GET call runs. Measures the - max gap between ticks. A blocking call (e.g. requests instead of aiohttp, - or time.sleep instead of asyncio.sleep) would starve the canary and produce - a gap equal to the full round-trip. Covers: records.list, tables.list, - tables.get, query.sql, query.fetchxml, query.builder. - -2. Read throughput (sequential vs concurrent) - Runs N reads sequentially then N reads with asyncio.gather(). Confirms the - HTTP GET path actually parallelizes. An internal lock or misplaced await - would collapse the speedup to ~1x. Covers: records.list, query.sql, - tables.get. - -3. Write concurrency (POST path) - Same as Test 2 but for records.create() (POST). The POST path uses a - different timeout branch (120 s vs 10 s for GET) and different server - behavior. A separate test ensures writes are also truly concurrent, not - just reads. Creates N records in parallel then cleans them up. - -4. Pagination non-blocking (async generator canary) - Runs list_pages(), fetchxml().execute_pages(), and builder().execute_pages() - while the canary ticks. Verifies the async generator yields control back to - the event loop between page fetches. A generator that does not await - properly between pages would block other tasks during multi-page queries. - -5. Mixed fan-out (cross-operation concurrency) - Fires 6 different operation types simultaneously in one gather(): records.list, - tables.get (x2), query.sql, query.fetchxml, query.builder. A shared internal - resource (metadata cache lock, single connection) could accidentally serialize - different operation types even if same-type parallelism works fine. This test - catches cross-operation serialization. - -6. Error resilience - Fires 5 calls — 3 good, 2 intentionally bad — using gather(return_exceptions=True). - Verifies the 3 good calls complete and return results despite the 2 failures. - Without return_exceptions=True, one exception cancels all in-flight coroutines. - Validates the correct usage pattern and confirms the SDK does not suppress - exceptions in a way that would break this pattern. - -7. Real-world metadata fan-out - Fetches schema info for 6 tables sequentially then in parallel. The most - common real-world async use case: an application needs metadata for several - tables at startup. Demonstrates the pattern works end-to-end with real results. +1. Non-blocking reads — canary ticks every 10 ms during GET calls; large gap + means the event loop was blocked. +2. Read throughput — N sequential vs N concurrent reads; confirms GET path + parallelizes. +3. Write concurrency — N sequential vs N concurrent creates; confirms POST + path (different timeout branch) also parallelizes. +4. Pagination non-blocking— canary ticks during list_pages/execute_pages; confirms + async generators yield between page fetches. +5. Mixed fan-out — 6 different operation types fired simultaneously; + catches cross-operation serialization. +6. Error resilience — 3 good + 2 bad calls in gather(return_exceptions=True); + confirms failures don't cancel successful calls. +7. Real-world fan-out — metadata for 6 tables fetched in parallel; end-to-end + validation of the most common async use case. How to interpret results ------------------------ From 562e8b78ae161f105ae4c2d979dbae828200a477 Mon Sep 17 00:00:00 2001 From: Abel Milash Date: Wed, 20 May 2026 11:33:56 -0700 Subject: [PATCH 27/34] Rewrite per-test docstring entries as concise 3-sentence descriptions Co-Authored-By: Claude Sonnet 4.6 --- .../aio/advanced/concurrency_benchmark.py | 48 +++++++++++++------ 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/examples/aio/advanced/concurrency_benchmark.py b/examples/aio/advanced/concurrency_benchmark.py index bb06f256..d57bb021 100644 --- a/examples/aio/advanced/concurrency_benchmark.py +++ b/examples/aio/advanced/concurrency_benchmark.py @@ -9,20 +9,40 @@ Tests ----- -1. Non-blocking reads — canary ticks every 10 ms during GET calls; large gap - means the event loop was blocked. -2. Read throughput — N sequential vs N concurrent reads; confirms GET path - parallelizes. -3. Write concurrency — N sequential vs N concurrent creates; confirms POST - path (different timeout branch) also parallelizes. -4. Pagination non-blocking— canary ticks during list_pages/execute_pages; confirms - async generators yield between page fetches. -5. Mixed fan-out — 6 different operation types fired simultaneously; - catches cross-operation serialization. -6. Error resilience — 3 good + 2 bad calls in gather(return_exceptions=True); - confirms failures don't cancel successful calls. -7. Real-world fan-out — metadata for 6 tables fetched in parallel; end-to-end - validation of the most common async use case. +1. Non-blocking reads (canary) + A background task ticks every 10 ms while each GET call runs. A blocking call + would starve the canary and produce a large gap. Covers records.list, + tables.list, tables.get, query.sql, query.fetchxml, query.builder. + +2. Read throughput (sequential vs concurrent) + Runs N reads sequentially then N reads with asyncio.gather(). An internal + lock or misplaced await would collapse the speedup to ~1x. Covers + records.list, query.sql, tables.get. + +3. Write concurrency (POST path) + Same as Test 2 but for records.create(). The POST path uses a different + timeout branch (120 s vs 10 s for GET), so a separate test ensures writes + are also truly concurrent. Creates N records in parallel then cleans up. + +4. Pagination non-blocking (async generator canary) + Runs list_pages(), fetchxml().execute_pages(), and builder().execute_pages() + while the canary ticks. Confirms the async generator yields control back to + the event loop between page fetches. + +5. Mixed fan-out (cross-operation concurrency) + Fires 6 different operation types simultaneously in one gather(). A shared + internal resource could accidentally serialize different operation types even + if same-type parallelism works. This test catches cross-operation serialization. + +6. Error resilience + Fires 5 calls — 3 good, 2 intentionally bad — using gather(return_exceptions=True). + Confirms the 3 good calls complete despite the 2 failures. Validates that the + SDK does not suppress exceptions in a way that would break this pattern. + +7. Real-world metadata fan-out + Fetches schema info for 6 tables sequentially then in parallel. The most + common real-world async use case: an app needs metadata for several tables + at startup. Demonstrates the pattern end-to-end with real results. How to interpret results ------------------------ From 4b0b1d5bcea8b24fe0ec73b8d36e52129d305beb Mon Sep 17 00:00:00 2001 From: Abel Milash Date: Sun, 24 May 2026 13:02:57 -0700 Subject: [PATCH 28/34] Add async package re-exports and apply doc fixes mirroring PR #165 Populate __all__ in aio, aio.models, and aio.operations __init__.py files so public symbols are importable directly from the package namespace. Update all async examples, README, and skill docs to use the shorter import paths. Add test_async_package_exports.py mirroring the sync test_package_exports.py. Co-Authored-By: Claude Sonnet 4.6 --- .claude/skills/dataverse-sdk-use/SKILL.md | 2 +- README.md | 6 +- .../aio/advanced/alternate_keys_upsert.py | 2 +- examples/aio/advanced/batch.py | 2 +- .../aio/advanced/concurrency_benchmark.py | 2 +- examples/aio/advanced/dataframe_operations.py | 2 +- .../advanced/datascience_risk_assessment.py | 2 +- examples/aio/advanced/fetchxml.py | 2 +- examples/aio/advanced/file_upload.py | 2 +- examples/aio/advanced/prodev_quick_start.py | 2 +- examples/aio/advanced/relationships.py | 2 +- examples/aio/advanced/sql_examples.py | 2 +- examples/aio/advanced/walkthrough.py | 2 +- examples/aio/basic/functional_testing.py | 2 +- examples/aio/basic/installation_example.py | 21 +-- src/PowerPlatform/Dataverse/aio/__init__.py | 6 +- .../Dataverse/aio/models/__init__.py | 8 +- .../aio/models/async_query_builder.py | 2 +- .../Dataverse/aio/operations/__init__.py | 20 ++- .../Dataverse/aio/operations/async_query.py | 4 +- .../Dataverse/aio/operations/async_records.py | 4 +- .../Dataverse/aio/operations/async_tables.py | 10 +- .../claude_skill/dataverse-sdk-use/SKILL.md | 2 +- tests/unit/aio/test_async_package_exports.py | 132 ++++++++++++++++++ 24 files changed, 198 insertions(+), 43 deletions(-) create mode 100644 tests/unit/aio/test_async_package_exports.py diff --git a/.claude/skills/dataverse-sdk-use/SKILL.md b/.claude/skills/dataverse-sdk-use/SKILL.md index 34ed35c3..28f792b7 100644 --- a/.claude/skills/dataverse-sdk-use/SKILL.md +++ b/.claude/skills/dataverse-sdk-use/SKILL.md @@ -595,7 +595,7 @@ The SDK ships a full async client, `AsyncDataverseClient`, under `PowerPlatform. ### Import ```python from azure.identity.aio import DefaultAzureCredential -from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient +from PowerPlatform.Dataverse.aio import AsyncDataverseClient ``` ### Client Initialization diff --git a/README.md b/README.md index f092054d..40892faf 100644 --- a/README.md +++ b/README.md @@ -801,7 +801,7 @@ pip install "PowerPlatform-Dataverse-Client[async]" ```python import asyncio from azure.identity.aio import DefaultAzureCredential -from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient +from PowerPlatform.Dataverse.aio import AsyncDataverseClient async def main(): async with DefaultAzureCredential() as credential: @@ -819,8 +819,6 @@ async def main(): asyncio.run(main()) ``` -> **Note:** `InteractiveBrowserCredential` from `azure.identity` is sync-only and cannot be used directly with the async client. See [examples/aio/_auth.py](https://github.com/microsoft/PowerPlatform-DataverseClient-Python/blob/main/examples/aio/_auth.py) for an async wrapper. - ### Standalone usage (without `async with`) ```python @@ -867,7 +865,7 @@ batch = client.batch.new() batch.records.create("account", {"name": "Alpha"}) batch.records.create("account", {"name": "Beta"}) result = await batch.execute() -print(f"Created {len(list(result.entity_ids))} records") +print(f"Created {len(result.entity_ids)} records") # Atomic changeset batch = client.batch.new() diff --git a/examples/aio/advanced/alternate_keys_upsert.py b/examples/aio/advanced/alternate_keys_upsert.py index 97ee221c..b061e35a 100644 --- a/examples/aio/advanced/alternate_keys_upsert.py +++ b/examples/aio/advanced/alternate_keys_upsert.py @@ -24,7 +24,7 @@ import asyncio import sys -from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient +from PowerPlatform.Dataverse.aio import AsyncDataverseClient from PowerPlatform.Dataverse.models.upsert import UpsertItem from pathlib import Path diff --git a/examples/aio/advanced/batch.py b/examples/aio/advanced/batch.py index 7023a85b..068761f1 100644 --- a/examples/aio/advanced/batch.py +++ b/examples/aio/advanced/batch.py @@ -22,7 +22,7 @@ sys.path.insert(0, str(Path(__file__).resolve().parents[1])) from _auth import AsyncInteractiveBrowserCredential -from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient +from PowerPlatform.Dataverse.aio import AsyncDataverseClient async def main(): diff --git a/examples/aio/advanced/concurrency_benchmark.py b/examples/aio/advanced/concurrency_benchmark.py index d57bb021..4fc965cd 100644 --- a/examples/aio/advanced/concurrency_benchmark.py +++ b/examples/aio/advanced/concurrency_benchmark.py @@ -70,7 +70,7 @@ sys.path.insert(0, str(Path(__file__).resolve().parents[1])) from _auth import AsyncInteractiveBrowserCredential -from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient +from PowerPlatform.Dataverse.aio import AsyncDataverseClient from PowerPlatform.Dataverse.models.record import QueryResult from PowerPlatform.Dataverse.models.table_info import TableInfo diff --git a/examples/aio/advanced/dataframe_operations.py b/examples/aio/advanced/dataframe_operations.py index 463f38fe..375fa7af 100644 --- a/examples/aio/advanced/dataframe_operations.py +++ b/examples/aio/advanced/dataframe_operations.py @@ -24,7 +24,7 @@ sys.path.insert(0, str(Path(__file__).resolve().parents[1])) from _auth import AsyncInteractiveBrowserCredential -from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient +from PowerPlatform.Dataverse.aio import AsyncDataverseClient from PowerPlatform.Dataverse.models.filters import col, raw diff --git a/examples/aio/advanced/datascience_risk_assessment.py b/examples/aio/advanced/datascience_risk_assessment.py index 0800837a..e3904a4c 100644 --- a/examples/aio/advanced/datascience_risk_assessment.py +++ b/examples/aio/advanced/datascience_risk_assessment.py @@ -57,7 +57,7 @@ sys.path.insert(0, str(Path(__file__).resolve().parents[1])) from _auth import AsyncInteractiveBrowserCredential -from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient +from PowerPlatform.Dataverse.aio import AsyncDataverseClient from PowerPlatform.Dataverse.models.filters import col, raw # -- Optional imports (graceful degradation if not installed) ------ diff --git a/examples/aio/advanced/fetchxml.py b/examples/aio/advanced/fetchxml.py index 95fd8811..f54e1226 100644 --- a/examples/aio/advanced/fetchxml.py +++ b/examples/aio/advanced/fetchxml.py @@ -34,7 +34,7 @@ sys.path.insert(0, str(Path(__file__).resolve().parents[1])) from _auth import AsyncInteractiveBrowserCredential -from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient +from PowerPlatform.Dataverse.aio import AsyncDataverseClient from PowerPlatform.Dataverse.core.errors import MetadataError diff --git a/examples/aio/advanced/file_upload.py b/examples/aio/advanced/file_upload.py index 2b57a0b5..47f74493 100644 --- a/examples/aio/advanced/file_upload.py +++ b/examples/aio/advanced/file_upload.py @@ -20,7 +20,7 @@ import traceback from pathlib import Path -from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient +from PowerPlatform.Dataverse.aio import AsyncDataverseClient sys.path.insert(0, str(Path(__file__).resolve().parents[1])) from _auth import AsyncInteractiveBrowserCredential diff --git a/examples/aio/advanced/prodev_quick_start.py b/examples/aio/advanced/prodev_quick_start.py index 9c10852f..e96b529d 100644 --- a/examples/aio/advanced/prodev_quick_start.py +++ b/examples/aio/advanced/prodev_quick_start.py @@ -60,7 +60,7 @@ sys.path.insert(0, str(Path(__file__).resolve().parents[1])) from _auth import AsyncInteractiveBrowserCredential -from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient +from PowerPlatform.Dataverse.aio import AsyncDataverseClient from PowerPlatform.Dataverse.models.filters import col # -- Table schema names -- diff --git a/examples/aio/advanced/relationships.py b/examples/aio/advanced/relationships.py index a4f9ecff..f87b979c 100644 --- a/examples/aio/advanced/relationships.py +++ b/examples/aio/advanced/relationships.py @@ -24,7 +24,7 @@ sys.path.insert(0, str(Path(__file__).resolve().parents[1])) from _auth import AsyncInteractiveBrowserCredential -from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient +from PowerPlatform.Dataverse.aio import AsyncDataverseClient from PowerPlatform.Dataverse.models.relationship import ( LookupAttributeMetadata, OneToManyRelationshipMetadata, diff --git a/examples/aio/advanced/sql_examples.py b/examples/aio/advanced/sql_examples.py index 3848b6c3..35378be5 100644 --- a/examples/aio/advanced/sql_examples.py +++ b/examples/aio/advanced/sql_examples.py @@ -50,7 +50,7 @@ sys.path.insert(0, str(Path(__file__).resolve().parents[1])) from _auth import AsyncInteractiveBrowserCredential -from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient +from PowerPlatform.Dataverse.aio import AsyncDataverseClient from PowerPlatform.Dataverse.core.errors import MetadataError # --------------------------------------------------------------------------- diff --git a/examples/aio/advanced/walkthrough.py b/examples/aio/advanced/walkthrough.py index d7a14e4a..2eaa99fc 100644 --- a/examples/aio/advanced/walkthrough.py +++ b/examples/aio/advanced/walkthrough.py @@ -30,7 +30,7 @@ sys.path.insert(0, str(Path(__file__).resolve().parents[1])) from _auth import AsyncInteractiveBrowserCredential -from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient +from PowerPlatform.Dataverse.aio import AsyncDataverseClient from PowerPlatform.Dataverse.core.errors import MetadataError from PowerPlatform.Dataverse.models.filters import col from PowerPlatform.Dataverse.models.query_builder import ExpandOption diff --git a/examples/aio/basic/functional_testing.py b/examples/aio/basic/functional_testing.py index 3f5d940f..4f078b76 100644 --- a/examples/aio/basic/functional_testing.py +++ b/examples/aio/basic/functional_testing.py @@ -28,7 +28,7 @@ from typing import Optional, Dict, Any from datetime import datetime -from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient +from PowerPlatform.Dataverse.aio import AsyncDataverseClient from PowerPlatform.Dataverse.core.errors import HttpError, MetadataError from PowerPlatform.Dataverse.models.relationship import ( LookupAttributeMetadata, diff --git a/examples/aio/basic/installation_example.py b/examples/aio/basic/installation_example.py index 46c97aa1..03cb5369 100644 --- a/examples/aio/basic/installation_example.py +++ b/examples/aio/basic/installation_example.py @@ -60,10 +60,12 @@ sys.path.insert(0, str(Path(__file__).resolve().parents[1])) -from PowerPlatform.Dataverse.aio.operations.async_records import AsyncRecordOperations -from PowerPlatform.Dataverse.aio.operations.async_query import AsyncQueryOperations -from PowerPlatform.Dataverse.aio.operations.async_tables import AsyncTableOperations -from PowerPlatform.Dataverse.aio.operations.async_files import AsyncFileOperations +from PowerPlatform.Dataverse.aio.operations import ( + AsyncFileOperations, + AsyncQueryOperations, + AsyncRecordOperations, + AsyncTableOperations, +) def validate_imports(): @@ -73,11 +75,11 @@ def validate_imports(): try: from PowerPlatform.Dataverse import __version__ - from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient + from PowerPlatform.Dataverse.aio import AsyncDataverseClient print(f" [OK] Namespace: PowerPlatform.Dataverse.aio") print(f" [OK] Package version: {__version__}") - print(f" [OK] Async client: PowerPlatform.Dataverse.aio.async_client.AsyncDataverseClient") + print(f" [OK] Async client: PowerPlatform.Dataverse.aio.AsyncDataverseClient") from PowerPlatform.Dataverse.core.errors import HttpError, MetadataError @@ -91,8 +93,7 @@ def validate_imports(): print(f" [OK] Async data layer: _AsyncODataClient") - from PowerPlatform.Dataverse.aio.models.async_fetchxml_query import AsyncFetchXmlQuery - from PowerPlatform.Dataverse.aio.models.async_query_builder import AsyncQueryBuilder + from PowerPlatform.Dataverse.aio.models import AsyncFetchXmlQuery, AsyncQueryBuilder print(f" [OK] Async models: AsyncFetchXmlQuery, AsyncQueryBuilder") @@ -192,7 +193,7 @@ def show_usage_examples(): from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parents[1])) from _auth import AsyncInteractiveBrowserCredential -from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient +from PowerPlatform.Dataverse.aio import AsyncDataverseClient async def main(): credential = AsyncInteractiveBrowserCredential() @@ -304,7 +305,7 @@ async def interactive_test(): return try: - from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient + from PowerPlatform.Dataverse.aio import AsyncDataverseClient from _auth import AsyncInteractiveBrowserCredential print(" Setting up authentication...") diff --git a/src/PowerPlatform/Dataverse/aio/__init__.py b/src/PowerPlatform/Dataverse/aio/__init__.py index ab39e8d8..b3d16558 100644 --- a/src/PowerPlatform/Dataverse/aio/__init__.py +++ b/src/PowerPlatform/Dataverse/aio/__init__.py @@ -6,7 +6,9 @@ Import the async client via:: - from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient + from PowerPlatform.Dataverse.aio import AsyncDataverseClient """ -__all__ = [] +from .async_client import AsyncDataverseClient + +__all__ = ["AsyncDataverseClient"] diff --git a/src/PowerPlatform/Dataverse/aio/models/__init__.py b/src/PowerPlatform/Dataverse/aio/models/__init__.py index f4c2e3d0..36ffc3dc 100644 --- a/src/PowerPlatform/Dataverse/aio/models/__init__.py +++ b/src/PowerPlatform/Dataverse/aio/models/__init__.py @@ -10,4 +10,10 @@ - :class:`~PowerPlatform.Dataverse.aio.models.async_fetchxml_query.AsyncFetchXmlQuery`: Async FetchXML query. """ -__all__ = [] +from .async_fetchxml_query import AsyncFetchXmlQuery +from .async_query_builder import AsyncQueryBuilder + +__all__ = [ + "AsyncFetchXmlQuery", + "AsyncQueryBuilder", +] diff --git a/src/PowerPlatform/Dataverse/aio/models/async_query_builder.py b/src/PowerPlatform/Dataverse/aio/models/async_query_builder.py index e48b3198..65b0bc42 100644 --- a/src/PowerPlatform/Dataverse/aio/models/async_query_builder.py +++ b/src/PowerPlatform/Dataverse/aio/models/async_query_builder.py @@ -25,7 +25,7 @@ class AsyncQueryBuilder(_QueryBuilderBase): Example:: - from PowerPlatform.Dataverse.models.filters import col + from PowerPlatform.Dataverse.models import col result = await (client.query.builder("account") .select("name", "revenue") diff --git a/src/PowerPlatform/Dataverse/aio/operations/__init__.py b/src/PowerPlatform/Dataverse/aio/operations/__init__.py index 62bb4281..c51cff67 100644 --- a/src/PowerPlatform/Dataverse/aio/operations/__init__.py +++ b/src/PowerPlatform/Dataverse/aio/operations/__init__.py @@ -8,6 +8,22 @@ SDK operations into logical groups: records, query, tables, files, and batch. """ -from typing import List +from .async_batch import AsyncBatchOperations, AsyncBatchRequest, AsyncChangeSet +from .async_dataframe import AsyncDataFrameOperations +from .async_files import AsyncFileOperations +from .async_query import AsyncQueryOperations +from .async_records import AsyncRecordOperations +from .async_tables import AsyncTableOperations -__all__: List[str] = [] +__all__ = [ + # batch + "AsyncBatchOperations", + "AsyncBatchRequest", + "AsyncChangeSet", + # other operations + "AsyncDataFrameOperations", + "AsyncFileOperations", + "AsyncQueryOperations", + "AsyncRecordOperations", + "AsyncTableOperations", +] diff --git a/src/PowerPlatform/Dataverse/aio/operations/async_query.py b/src/PowerPlatform/Dataverse/aio/operations/async_query.py index 67fcb84f..21982d35 100644 --- a/src/PowerPlatform/Dataverse/aio/operations/async_query.py +++ b/src/PowerPlatform/Dataverse/aio/operations/async_query.py @@ -36,7 +36,7 @@ class AsyncQueryOperations: async with AsyncDataverseClient(base_url, credential) as client: # Fluent query builder (recommended) - from PowerPlatform.Dataverse.models.filters import col + from PowerPlatform.Dataverse.models import col for record in await (client.query.builder("account") .select("name", "revenue") @@ -72,7 +72,7 @@ def builder(self, table: str) -> AsyncQueryBuilder: Example:: - from PowerPlatform.Dataverse.models.filters import col + from PowerPlatform.Dataverse.models import col result = await (client.query.builder("account") .select("name", "revenue") diff --git a/src/PowerPlatform/Dataverse/aio/operations/async_records.py b/src/PowerPlatform/Dataverse/aio/operations/async_records.py index 1a2d86cf..ba50da35 100644 --- a/src/PowerPlatform/Dataverse/aio/operations/async_records.py +++ b/src/PowerPlatform/Dataverse/aio/operations/async_records.py @@ -450,7 +450,7 @@ async def upsert(self, table: str, items: List[Union[UpsertItem, Dict[str, Any]] Example: Upsert a single record using ``UpsertItem``:: - from PowerPlatform.Dataverse.models.upsert import UpsertItem + from PowerPlatform.Dataverse.models import UpsertItem await client.records.upsert("account", [ UpsertItem( @@ -470,7 +470,7 @@ async def upsert(self, table: str, items: List[Union[UpsertItem, Dict[str, Any]] Upsert multiple records using ``UpsertItem``:: - from PowerPlatform.Dataverse.models.upsert import UpsertItem + from PowerPlatform.Dataverse.models import UpsertItem await client.records.upsert("account", [ UpsertItem( diff --git a/src/PowerPlatform/Dataverse/aio/operations/async_tables.py b/src/PowerPlatform/Dataverse/aio/operations/async_tables.py index 0fbe61c8..bdf736b1 100644 --- a/src/PowerPlatform/Dataverse/aio/operations/async_tables.py +++ b/src/PowerPlatform/Dataverse/aio/operations/async_tables.py @@ -343,12 +343,12 @@ async def create_one_to_many_relationship( Example: Create a one-to-many relationship: Department (1) -> Employee (N):: - from PowerPlatform.Dataverse.models.relationship import ( - LookupAttributeMetadata, - OneToManyRelationshipMetadata, + from PowerPlatform.Dataverse.models import ( + CascadeConfiguration, Label, LocalizedLabel, - CascadeConfiguration, + LookupAttributeMetadata, + OneToManyRelationshipMetadata, ) from PowerPlatform.Dataverse.common.constants import ( CASCADE_BEHAVIOR_REMOVE_LINK, @@ -419,7 +419,7 @@ async def create_many_to_many_relationship( Example: Create a many-to-many relationship: Employee <-> Project:: - from PowerPlatform.Dataverse.models.relationship import ( + from PowerPlatform.Dataverse.models import ( ManyToManyRelationshipMetadata, ) diff --git a/src/PowerPlatform/Dataverse/claude_skill/dataverse-sdk-use/SKILL.md b/src/PowerPlatform/Dataverse/claude_skill/dataverse-sdk-use/SKILL.md index 34ed35c3..28f792b7 100644 --- a/src/PowerPlatform/Dataverse/claude_skill/dataverse-sdk-use/SKILL.md +++ b/src/PowerPlatform/Dataverse/claude_skill/dataverse-sdk-use/SKILL.md @@ -595,7 +595,7 @@ The SDK ships a full async client, `AsyncDataverseClient`, under `PowerPlatform. ### Import ```python from azure.identity.aio import DefaultAzureCredential -from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient +from PowerPlatform.Dataverse.aio import AsyncDataverseClient ``` ### Client Initialization diff --git a/tests/unit/aio/test_async_package_exports.py b/tests/unit/aio/test_async_package_exports.py new file mode 100644 index 00000000..8e8de824 --- /dev/null +++ b/tests/unit/aio/test_async_package_exports.py @@ -0,0 +1,132 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +"""Tests for async package-level re-exports. + +Each async package (``PowerPlatform.Dataverse.aio``, +``aio.operations``, ``aio.models``) re-exports its public symbols +and declares them in ``__all__``. This gives users short, stable +import paths (``from PowerPlatform.Dataverse.aio import AsyncDataverseClient``) +that survive internal module reorganization. + +These tests verify: +1. ``__all__`` matches the expected list exactly (catches accidental drift). +2. Every name in ``__all__`` is importable from the package namespace. +3. Each re-export is the same object as its source definition. +""" + +import unittest + +AIO_EXPECTED = [ + "AsyncDataverseClient", +] + +AIO_OPERATIONS_EXPECTED = [ + "AsyncBatchOperations", + "AsyncBatchRequest", + "AsyncChangeSet", + "AsyncDataFrameOperations", + "AsyncFileOperations", + "AsyncQueryOperations", + "AsyncRecordOperations", + "AsyncTableOperations", +] + +AIO_MODELS_EXPECTED = [ + "AsyncFetchXmlQuery", + "AsyncQueryBuilder", +] + + +class TestAioTopLevelExports(unittest.TestCase): + """Verify top-level PowerPlatform.Dataverse.aio package exports.""" + + def test_all_matches_expected(self): + """``__all__`` matches the expected list exactly.""" + import PowerPlatform.Dataverse.aio as m + + self.assertEqual(sorted(m.__all__), sorted(AIO_EXPECTED)) + + def test_expected_symbols_importable(self): + """Every expected public symbol is reachable from the package namespace.""" + import PowerPlatform.Dataverse.aio as m + + for name in AIO_EXPECTED: + self.assertTrue(hasattr(m, name), f"{name!r} not importable from PowerPlatform.Dataverse.aio") + + def test_identity(self): + """Re-exported objects are the same objects as their source definitions.""" + import PowerPlatform.Dataverse.aio as m + from PowerPlatform.Dataverse.aio.async_client import AsyncDataverseClient + + self.assertIs(m.AsyncDataverseClient, AsyncDataverseClient) + + +class TestAioOperationsExports(unittest.TestCase): + """Verify package-level imports for PowerPlatform.Dataverse.aio.operations.""" + + def test_all_matches_expected(self): + """``__all__`` matches the expected list exactly.""" + import PowerPlatform.Dataverse.aio.operations as m + + self.assertEqual(sorted(m.__all__), sorted(AIO_OPERATIONS_EXPECTED)) + + def test_expected_symbols_importable(self): + """Every expected public symbol is reachable from the package namespace.""" + import PowerPlatform.Dataverse.aio.operations as m + + for name in AIO_OPERATIONS_EXPECTED: + self.assertTrue(hasattr(m, name), f"{name!r} not importable from PowerPlatform.Dataverse.aio.operations") + + def test_identity(self): + """Re-exported objects are the same objects as their source definitions.""" + import PowerPlatform.Dataverse.aio.operations as m + from PowerPlatform.Dataverse.aio.operations.async_batch import ( + AsyncBatchOperations, + AsyncBatchRequest, + AsyncChangeSet, + ) + from PowerPlatform.Dataverse.aio.operations.async_dataframe import AsyncDataFrameOperations + from PowerPlatform.Dataverse.aio.operations.async_files import AsyncFileOperations + from PowerPlatform.Dataverse.aio.operations.async_query import AsyncQueryOperations + from PowerPlatform.Dataverse.aio.operations.async_records import AsyncRecordOperations + from PowerPlatform.Dataverse.aio.operations.async_tables import AsyncTableOperations + + self.assertIs(m.AsyncBatchOperations, AsyncBatchOperations) + self.assertIs(m.AsyncBatchRequest, AsyncBatchRequest) + self.assertIs(m.AsyncChangeSet, AsyncChangeSet) + self.assertIs(m.AsyncDataFrameOperations, AsyncDataFrameOperations) + self.assertIs(m.AsyncFileOperations, AsyncFileOperations) + self.assertIs(m.AsyncQueryOperations, AsyncQueryOperations) + self.assertIs(m.AsyncRecordOperations, AsyncRecordOperations) + self.assertIs(m.AsyncTableOperations, AsyncTableOperations) + + +class TestAioModelsExports(unittest.TestCase): + """Verify package-level imports for PowerPlatform.Dataverse.aio.models.""" + + def test_all_matches_expected(self): + """``__all__`` matches the expected list exactly.""" + import PowerPlatform.Dataverse.aio.models as m + + self.assertEqual(sorted(m.__all__), sorted(AIO_MODELS_EXPECTED)) + + def test_expected_symbols_importable(self): + """Every expected public symbol is reachable from the package namespace.""" + import PowerPlatform.Dataverse.aio.models as m + + for name in AIO_MODELS_EXPECTED: + self.assertTrue(hasattr(m, name), f"{name!r} not importable from PowerPlatform.Dataverse.aio.models") + + def test_identity(self): + """Re-exported objects are the same objects as their source definitions.""" + import PowerPlatform.Dataverse.aio.models as m + from PowerPlatform.Dataverse.aio.models.async_fetchxml_query import AsyncFetchXmlQuery + from PowerPlatform.Dataverse.aio.models.async_query_builder import AsyncQueryBuilder + + self.assertIs(m.AsyncFetchXmlQuery, AsyncFetchXmlQuery) + self.assertIs(m.AsyncQueryBuilder, AsyncQueryBuilder) + + +if __name__ == "__main__": + unittest.main() From d9fb292123d432838ff165a38c7ca6f1a2ab497e Mon Sep 17 00:00:00 2001 From: Abel Milash Date: Sun, 24 May 2026 13:09:56 -0700 Subject: [PATCH 29/34] Fix test_config_alone_works: use valid skill value 'dv-data' 'dv' is not in _ALLOWED_SKILLS (added to config.py on main). The PR build tests the merged result so it picked up the validation. Update to use 'dv-data' which is an allowed value. Co-Authored-By: Claude Sonnet 4.6 --- tests/unit/aio/test_async_client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/aio/test_async_client.py b/tests/unit/aio/test_async_client.py index 5997a30a..36f18674 100644 --- a/tests/unit/aio/test_async_client.py +++ b/tests/unit/aio/test_async_client.py @@ -235,7 +235,7 @@ def test_config_and_context_raises(self): def test_config_alone_works(self): """Providing config= without context= uses config's operation_context.""" - ctx = OperationContext(user_agent_context="app=test/1.0;skill=dv") + ctx = OperationContext(user_agent_context="app=test/1.0;skill=dv-data") config = DataverseConfig(operation_context=ctx) client = AsyncDataverseClient("https://org.crm.dynamics.com", _make_credential(), config=config) - assert client._config.operation_context.user_agent_context == "app=test/1.0;skill=dv" + assert client._config.operation_context.user_agent_context == "app=test/1.0;skill=dv-data" From c1f12374e0b31e14552c29b61f349e40d9f5caca Mon Sep 17 00:00:00 2001 From: Abel Milash Date: Sun, 24 May 2026 14:45:08 -0700 Subject: [PATCH 30/34] Mirror PR #183 (CreateEntities API) in async client MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sync side was updated to use the new CreateEntities API in PR #183. The async client has its own copy of _create_entity and _create_table, so we need to mirror those changes manually: - _AsyncODataClient._create_entity(): URL EntityDefinitions -> CreateEntities, payload wrapped in Entities[0] array with @odata.type ComplexEntityMetadata - _AsyncODataClient._create_table(): pass complex=True to _attribute_payload calls so attribute metadata uses the Complex*Metadata variants required by CreateEntities The shared base (_odata_base.py) changes from PR #183 — including the _attribute_payload(complex=...) parameter and Complex*Metadata output — flow into the async client automatically via inheritance. Also picks up PR #181 (OperationContext key/value allowlisting) via the same main merge. Tests: 2187 pass, black clean. Co-Authored-By: Claude Opus 4.7 (1M context) --- .claude/skills/dataverse-sdk-release/SKILL.md | 163 + .../async_jupyter_demo-checkpoint.ipynb | 104 + _check_exports.py | 51 + _check_imports.py | 44 + _tee_run.py | 41 + async_jupyter_demo.ipynb | 120 + coverage.json | 1 + coverage.xml | 3181 +++++++++++++++++ coverage_aio.json | 1 + docs/async-design-options.md | 288 ++ docs/async-design.md | 276 ++ docs/design-evaluation.md | 344 ++ docs/proposal-typed-model-alignment.md | 277 ++ docs_local/_build/.buildinfo | 4 + .../Dataverse/claude_skill/index.doctree | Bin 0 -> 3639 bytes .../Dataverse/client/index.doctree | Bin 0 -> 43856 bytes .../Dataverse/common/constants/index.doctree | Bin 0 -> 30710 bytes .../Dataverse/common/index.doctree | Bin 0 -> 3972 bytes .../Dataverse/core/config/index.doctree | Bin 0 -> 38957 bytes .../Dataverse/core/errors/index.doctree | Bin 0 -> 103492 bytes .../Dataverse/core/index.doctree | Bin 0 -> 4171 bytes .../Dataverse/core/log_config/index.doctree | Bin 0 -> 27189 bytes .../Dataverse/data/index.doctree | Bin 0 -> 3445 bytes .../Dataverse/extensions/index.doctree | Bin 0 -> 3175 bytes .../PowerPlatform/Dataverse/index.doctree | Bin 0 -> 3944 bytes .../Dataverse/migration/index.doctree | Bin 0 -> 3559 bytes .../migration/migrate_v0_to_v1/index.doctree | Bin 0 -> 42581 bytes .../Dataverse/models/batch/index.doctree | Bin 0 -> 44755 bytes .../models/fetchxml_query/index.doctree | Bin 0 -> 24175 bytes .../Dataverse/models/filters/index.doctree | Bin 0 -> 146068 bytes .../Dataverse/models/index.doctree | Bin 0 -> 10245 bytes .../Dataverse/models/labels/index.doctree | Bin 0 -> 36905 bytes .../Dataverse/models/protocol/index.doctree | Bin 0 -> 18057 bytes .../models/query_builder/index.doctree | Bin 0 -> 85179 bytes .../Dataverse/models/record/index.doctree | Bin 0 -> 53127 bytes .../models/relationship/index.doctree | Bin 0 -> 161553 bytes .../Dataverse/models/table_info/index.doctree | Bin 0 -> 106688 bytes .../Dataverse/models/upsert/index.doctree | Bin 0 -> 16866 bytes .../Dataverse/operations/batch/index.doctree | Bin 0 -> 324887 bytes .../operations/dataframe/index.doctree | Bin 0 -> 90271 bytes .../Dataverse/operations/files/index.doctree | Bin 0 -> 30164 bytes .../Dataverse/operations/index.doctree | Bin 0 -> 4419 bytes .../Dataverse/operations/query/index.doctree | Bin 0 -> 86358 bytes .../operations/records/index.doctree | Bin 0 -> 201405 bytes .../Dataverse/operations/tables/index.doctree | Bin 0 -> 213594 bytes .../Dataverse/utils/index.doctree | Bin 0 -> 3280 bytes .../autoapi/PowerPlatform/index.doctree | Bin 0 -> 3372 bytes .../_build/.doctrees/autoapi/index.doctree | Bin 0 -> 4199 bytes .../_build/.doctrees/environment.pickle | Bin 0 -> 2482769 bytes docs_local/_build/.doctrees/index.doctree | Bin 0 -> 2971 bytes .../Dataverse/claude_skill/index.rst.txt | 15 + .../Dataverse/client/index.rst.txt | 157 + .../Dataverse/common/constants/index.rst.txt | 77 + .../Dataverse/common/index.rst.txt | 22 + .../Dataverse/core/config/index.rst.txt | 117 + .../Dataverse/core/errors/index.rst.txt | 185 + .../Dataverse/core/index.rst.txt | 25 + .../Dataverse/core/log_config/index.rst.txt | 90 + .../Dataverse/data/index.rst.txt | 14 + .../Dataverse/extensions/index.rst.txt | 11 + .../PowerPlatform/Dataverse/index.rst.txt | 24 + .../Dataverse/migration/index.rst.txt | 15 + .../migration/migrate_v0_to_v1/index.rst.txt | 119 + .../Dataverse/models/batch/index.rst.txt | 154 + .../models/fetchxml_query/index.rst.txt | 76 + .../Dataverse/models/filters/index.rst.txt | 365 ++ .../Dataverse/models/index.rst.txt | 41 + .../Dataverse/models/labels/index.rst.txt | 113 + .../Dataverse/models/protocol/index.rst.txt | 94 + .../models/query_builder/index.rst.txt | 332 ++ .../Dataverse/models/record/index.rst.txt | 152 + .../models/relationship/index.rst.txt | 471 +++ .../Dataverse/models/table_info/index.rst.txt | 305 ++ .../Dataverse/models/upsert/index.rst.txt | 54 + .../Dataverse/operations/batch/index.rst.txt | 741 ++++ .../operations/dataframe/index.rst.txt | 279 ++ .../Dataverse/operations/files/index.rst.txt | 99 + .../Dataverse/operations/index.rst.txt | 28 + .../Dataverse/operations/query/index.rst.txt | 345 ++ .../operations/records/index.rst.txt | 461 +++ .../Dataverse/operations/tables/index.rst.txt | 683 ++++ .../Dataverse/utils/index.rst.txt | 13 + .../autoapi/PowerPlatform/index.rst.txt | 15 + .../_build/_sources/autoapi/index.rst.txt | 11 + docs_local/_build/_sources/index.rst.txt | 8 + docs_local/_build/_static/alabaster.css | 708 ++++ docs_local/_build/_static/base-stemmer.js | 476 +++ docs_local/_build/_static/basic.css | 906 +++++ docs_local/_build/_static/custom.css | 1 + docs_local/_build/_static/doctools.js | 150 + .../_build/_static/documentation_options.js | 13 + docs_local/_build/_static/english-stemmer.js | 1066 ++++++ docs_local/_build/_static/file.png | Bin 0 -> 286 bytes docs_local/_build/_static/graphviz.css | 12 + docs_local/_build/_static/language_data.js | 13 + docs_local/_build/_static/minus.png | Bin 0 -> 90 bytes docs_local/_build/_static/plus.png | Bin 0 -> 90 bytes docs_local/_build/_static/pygments.css | 84 + docs_local/_build/_static/searchtools.js | 693 ++++ docs_local/_build/_static/sphinx_highlight.js | 159 + .../Dataverse/claude_skill/index.html | 122 + .../PowerPlatform/Dataverse/client/index.html | 295 ++ .../Dataverse/common/constants/index.html | 209 ++ .../PowerPlatform/Dataverse/common/index.html | 128 + .../Dataverse/core/config/index.html | 231 ++ .../Dataverse/core/errors/index.html | 304 ++ .../PowerPlatform/Dataverse/core/index.html | 131 + .../Dataverse/core/log_config/index.html | 201 ++ .../PowerPlatform/Dataverse/data/index.html | 121 + .../Dataverse/extensions/index.html | 119 + .../PowerPlatform/Dataverse/index.html | 133 + .../Dataverse/migration/index.html | 126 + .../migration/migrate_v0_to_v1/index.html | 247 ++ .../Dataverse/models/batch/index.html | 267 ++ .../models/fetchxml_query/index.html | 201 ++ .../Dataverse/models/filters/index.html | 550 +++ .../PowerPlatform/Dataverse/models/index.html | 148 + .../Dataverse/models/labels/index.html | 233 ++ .../Dataverse/models/protocol/index.html | 202 ++ .../Dataverse/models/query_builder/index.html | 504 +++ .../Dataverse/models/record/index.html | 273 ++ .../Dataverse/models/relationship/index.html | 586 +++ .../Dataverse/models/table_info/index.html | 403 +++ .../Dataverse/models/upsert/index.html | 171 + .../Dataverse/operations/batch/index.html | 932 +++++ .../Dataverse/operations/dataframe/index.html | 413 +++ .../Dataverse/operations/files/index.html | 212 ++ .../Dataverse/operations/index.html | 134 + .../Dataverse/operations/query/index.html | 497 +++ .../Dataverse/operations/records/index.html | 588 +++ .../Dataverse/operations/tables/index.html | 865 +++++ .../PowerPlatform/Dataverse/utils/index.html | 118 + .../_build/autoapi/PowerPlatform/index.html | 122 + docs_local/_build/autoapi/index.html | 170 + docs_local/_build/genindex.html | 1331 +++++++ docs_local/_build/index.html | 116 + docs_local/_build/objects.inv | Bin 0 -> 3506 bytes docs_local/_build/py-modindex.html | 279 ++ docs_local/_build/search.html | 121 + docs_local/_build/searchindex.js | 1 + .../Dataverse/claude_skill/index.rst | 15 + .../PowerPlatform/Dataverse/client/index.rst | 157 + .../Dataverse/common/constants/index.rst | 77 + .../PowerPlatform/Dataverse/common/index.rst | 22 + .../Dataverse/core/config/index.rst | 117 + .../Dataverse/core/errors/index.rst | 185 + .../PowerPlatform/Dataverse/core/index.rst | 25 + .../Dataverse/core/log_config/index.rst | 90 + .../PowerPlatform/Dataverse/data/index.rst | 14 + .../Dataverse/extensions/index.rst | 11 + .../autoapi/PowerPlatform/Dataverse/index.rst | 24 + .../Dataverse/migration/index.rst | 15 + .../migration/migrate_v0_to_v1/index.rst | 119 + .../Dataverse/models/batch/index.rst | 154 + .../Dataverse/models/fetchxml_query/index.rst | 76 + .../Dataverse/models/filters/index.rst | 365 ++ .../PowerPlatform/Dataverse/models/index.rst | 41 + .../Dataverse/models/labels/index.rst | 113 + .../Dataverse/models/protocol/index.rst | 94 + .../Dataverse/models/query_builder/index.rst | 332 ++ .../Dataverse/models/record/index.rst | 152 + .../Dataverse/models/relationship/index.rst | 471 +++ .../Dataverse/models/table_info/index.rst | 305 ++ .../Dataverse/models/upsert/index.rst | 54 + .../Dataverse/operations/batch/index.rst | 741 ++++ .../Dataverse/operations/dataframe/index.rst | 279 ++ .../Dataverse/operations/files/index.rst | 99 + .../Dataverse/operations/index.rst | 28 + .../Dataverse/operations/query/index.rst | 345 ++ .../Dataverse/operations/records/index.rst | 461 +++ .../Dataverse/operations/tables/index.rst | 683 ++++ .../PowerPlatform/Dataverse/utils/index.rst | 13 + docs_local/autoapi/PowerPlatform/index.rst | 15 + docs_local/autoapi/index.rst | 11 + docs_local/build.log | 216 ++ docs_local/build_after_fixes.log | 125 + docs_local/build_final.log | 130 + docs_local/build_no_imported.log | 120 + docs_local/build_option1.log | 140 + docs_local/build_optionA.log | 200 ++ docs_local/build_post_merge.log | 134 + docs_local/build_post_restore.log | 187 + docs_local/build_review.log | 120 + docs_local/conf.py | 41 + docs_local/index.rst | 8 + examples/advanced/memo_walkthrough.py | 426 +++ examples/advanced/perf_benchmark_live.py | 255 ++ examples/advanced/perf_benchmark_mock.py | 309 ++ examples/advanced/picklist_walkthrough.py | 440 +++ examples/advanced/test_querybuilder_live.py | 955 +++++ forge.txt | 557 +++ functional_testing_log.txt | 191 + functional_testing_output.txt | 24 + logs/log.txt | 245 ++ logs/package_logs/None.txt | 4 + .../PowerPlatform-Dataverse-Client.txt | 60 + scratch_async_demo.py | 82 + sql_examples_output.txt | 7 + .../Dataverse/aio/data/_async_odata.py | 30 +- 199 files changed, 37387 insertions(+), 13 deletions(-) create mode 100644 .claude/skills/dataverse-sdk-release/SKILL.md create mode 100644 .ipynb_checkpoints/async_jupyter_demo-checkpoint.ipynb create mode 100644 _check_exports.py create mode 100644 _check_imports.py create mode 100644 _tee_run.py create mode 100644 async_jupyter_demo.ipynb create mode 100644 coverage.json create mode 100644 coverage.xml create mode 100644 coverage_aio.json create mode 100644 docs/async-design-options.md create mode 100644 docs/async-design.md create mode 100644 docs/design-evaluation.md create mode 100644 docs/proposal-typed-model-alignment.md create mode 100644 docs_local/_build/.buildinfo create mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/claude_skill/index.doctree create mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/client/index.doctree create mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/common/constants/index.doctree create mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/common/index.doctree create mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/core/config/index.doctree create mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/core/errors/index.doctree create mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/core/index.doctree create mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/core/log_config/index.doctree create mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/data/index.doctree create mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/extensions/index.doctree create mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/index.doctree create mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/migration/index.doctree create mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index.doctree create mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/batch/index.doctree create mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index.doctree create mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/filters/index.doctree create mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/index.doctree create mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/labels/index.doctree create mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/protocol/index.doctree create mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/query_builder/index.doctree create mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/record/index.doctree create mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/relationship/index.doctree create mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/table_info/index.doctree create mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/upsert/index.doctree create mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/operations/batch/index.doctree create mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/operations/dataframe/index.doctree create mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/operations/files/index.doctree create mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/operations/index.doctree create mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/operations/query/index.doctree create mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/operations/records/index.doctree create mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/operations/tables/index.doctree create mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/utils/index.doctree create mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/index.doctree create mode 100644 docs_local/_build/.doctrees/autoapi/index.doctree create mode 100644 docs_local/_build/.doctrees/environment.pickle create mode 100644 docs_local/_build/.doctrees/index.doctree create mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/claude_skill/index.rst.txt create mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/client/index.rst.txt create mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/common/constants/index.rst.txt create mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/common/index.rst.txt create mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/core/config/index.rst.txt create mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/core/errors/index.rst.txt create mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/core/index.rst.txt create mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/core/log_config/index.rst.txt create mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/data/index.rst.txt create mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/extensions/index.rst.txt create mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/index.rst.txt create mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/migration/index.rst.txt create mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index.rst.txt create mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/batch/index.rst.txt create mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index.rst.txt create mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/filters/index.rst.txt create mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/index.rst.txt create mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/labels/index.rst.txt create mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/protocol/index.rst.txt create mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/query_builder/index.rst.txt create mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/record/index.rst.txt create mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/relationship/index.rst.txt create mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/table_info/index.rst.txt create mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/upsert/index.rst.txt create mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/batch/index.rst.txt create mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/dataframe/index.rst.txt create mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/files/index.rst.txt create mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/index.rst.txt create mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/query/index.rst.txt create mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/records/index.rst.txt create mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/tables/index.rst.txt create mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/utils/index.rst.txt create mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/index.rst.txt create mode 100644 docs_local/_build/_sources/autoapi/index.rst.txt create mode 100644 docs_local/_build/_sources/index.rst.txt create mode 100644 docs_local/_build/_static/alabaster.css create mode 100644 docs_local/_build/_static/base-stemmer.js create mode 100644 docs_local/_build/_static/basic.css create mode 100644 docs_local/_build/_static/custom.css create mode 100644 docs_local/_build/_static/doctools.js create mode 100644 docs_local/_build/_static/documentation_options.js create mode 100644 docs_local/_build/_static/english-stemmer.js create mode 100644 docs_local/_build/_static/file.png create mode 100644 docs_local/_build/_static/graphviz.css create mode 100644 docs_local/_build/_static/language_data.js create mode 100644 docs_local/_build/_static/minus.png create mode 100644 docs_local/_build/_static/plus.png create mode 100644 docs_local/_build/_static/pygments.css create mode 100644 docs_local/_build/_static/searchtools.js create mode 100644 docs_local/_build/_static/sphinx_highlight.js create mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/claude_skill/index.html create mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/client/index.html create mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/common/constants/index.html create mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/common/index.html create mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/core/config/index.html create mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/core/errors/index.html create mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/core/index.html create mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/core/log_config/index.html create mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/data/index.html create mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/extensions/index.html create mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/index.html create mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/migration/index.html create mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index.html create mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/models/batch/index.html create mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index.html create mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/models/filters/index.html create mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/models/index.html create mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/models/labels/index.html create mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/models/protocol/index.html create mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/models/query_builder/index.html create mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/models/record/index.html create mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/models/relationship/index.html create mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/models/table_info/index.html create mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/models/upsert/index.html create mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/batch/index.html create mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/dataframe/index.html create mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/files/index.html create mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/index.html create mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/query/index.html create mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/records/index.html create mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/tables/index.html create mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/utils/index.html create mode 100644 docs_local/_build/autoapi/PowerPlatform/index.html create mode 100644 docs_local/_build/autoapi/index.html create mode 100644 docs_local/_build/genindex.html create mode 100644 docs_local/_build/index.html create mode 100644 docs_local/_build/objects.inv create mode 100644 docs_local/_build/py-modindex.html create mode 100644 docs_local/_build/search.html create mode 100644 docs_local/_build/searchindex.js create mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/claude_skill/index.rst create mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/client/index.rst create mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/common/constants/index.rst create mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/common/index.rst create mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/core/config/index.rst create mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/core/errors/index.rst create mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/core/index.rst create mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/core/log_config/index.rst create mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/data/index.rst create mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/extensions/index.rst create mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/index.rst create mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/migration/index.rst create mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index.rst create mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/models/batch/index.rst create mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index.rst create mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/models/filters/index.rst create mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/models/index.rst create mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/models/labels/index.rst create mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/models/protocol/index.rst create mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/models/query_builder/index.rst create mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/models/record/index.rst create mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/models/relationship/index.rst create mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/models/table_info/index.rst create mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/models/upsert/index.rst create mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/operations/batch/index.rst create mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/operations/dataframe/index.rst create mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/operations/files/index.rst create mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/operations/index.rst create mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/operations/query/index.rst create mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/operations/records/index.rst create mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/operations/tables/index.rst create mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/utils/index.rst create mode 100644 docs_local/autoapi/PowerPlatform/index.rst create mode 100644 docs_local/autoapi/index.rst create mode 100644 docs_local/build.log create mode 100644 docs_local/build_after_fixes.log create mode 100644 docs_local/build_final.log create mode 100644 docs_local/build_no_imported.log create mode 100644 docs_local/build_option1.log create mode 100644 docs_local/build_optionA.log create mode 100644 docs_local/build_post_merge.log create mode 100644 docs_local/build_post_restore.log create mode 100644 docs_local/build_review.log create mode 100644 docs_local/conf.py create mode 100644 docs_local/index.rst create mode 100644 examples/advanced/memo_walkthrough.py create mode 100644 examples/advanced/perf_benchmark_live.py create mode 100644 examples/advanced/perf_benchmark_mock.py create mode 100644 examples/advanced/picklist_walkthrough.py create mode 100644 examples/advanced/test_querybuilder_live.py create mode 100644 forge.txt create mode 100644 functional_testing_log.txt create mode 100644 functional_testing_output.txt create mode 100644 logs/log.txt create mode 100644 logs/package_logs/None.txt create mode 100644 logs/package_logs/PowerPlatform-Dataverse-Client.txt create mode 100644 scratch_async_demo.py create mode 100644 sql_examples_output.txt diff --git a/.claude/skills/dataverse-sdk-release/SKILL.md b/.claude/skills/dataverse-sdk-release/SKILL.md new file mode 100644 index 00000000..dd99d21f --- /dev/null +++ b/.claude/skills/dataverse-sdk-release/SKILL.md @@ -0,0 +1,163 @@ +# Dataverse Python SDK Release Guide + +Step-by-step release process for the PowerPlatform Dataverse Client Python SDK. + +**SDK Repository**: [microsoft/PowerPlatform-DataverseClient-Python](https://github.com/microsoft/PowerPlatform-DataverseClient-Python) +**PyPI Package**: [PowerPlatform-Dataverse-Client](https://pypi.org/project/PowerPlatform-Dataverse-Client/) + +--- + +## Prerequisites + +- Write access to the GitHub repository (microsoft/PowerPlatform-DataverseClient-Python) +- Access to the Azure DevOps CI/CD pipeline for PyPI publishing: https://dev.azure.com/dynamicscrm/OneCRM/_build?definitionId=29949 +- _(Optional)_ **GitHub CLI (`gh`)** — enables automated PR creation and GitHub releases from the terminal. + - Install: `winget install GitHub.cli`, then `gh auth login`. + +--- + +## Release Checklist + +### Step 1: Identify Changes Since Last Release + +1. Find the last release version and date in CHANGELOG.md (the current dev version is in `pyproject.toml` under `version`) +2. List merged PRs since the last release: + - GitHub UI: https://github.com/microsoft/PowerPlatform-DataverseClient-Python/pulls?q=is%3Apr+is%3Amerged + - Or via git: `git log --oneline --since=""` +3. For each PR, read the full description to understand the user-facing impact + +### Step 2: Update CHANGELOG.md + +1. Create a branch: `git checkout -b release/v` (e.g., `release/v0.1.0b6`) +2. Add a new section at the top of CHANGELOG.md (below the header), using today's date: + +```markdown +## [X.Y.Z] - YYYY-MM-DD + +### Added +- Description of new feature (#PR_NUMBER) + +### Fixed +- Description of bug fix (#PR_NUMBER) +``` + +3. Add a version comparison link at the bottom of CHANGELOG.md: + +```markdown +[X.Y.Z]: https://github.com/microsoft/PowerPlatform-DataverseClient-Python/compare/vPREVIOUS...vX.Y.Z +``` + +**Changelog writing rules:** + +- Focus on **why it matters to users**, not implementation details +- Do NOT reference internal function names or implementation choices +- DO describe the user-visible behavior change or new capability +- Include PR numbers for reference: `(#123)` + +**Category headings:** +- New features → **Added** +- Changes to existing functionality → **Changed** +- Soon-to-be removed features → **Deprecated** +- Removed features → **Removed** +- Bug fixes → **Fixed** +- Security fixes → **Security** + +**What to exclude:** +- Internal refactoring (unless it affects performance/behavior) +- Test-only changes +- CI/CD changes +- Documentation-only updates + +### Step 3: Create PR for Changelog + +1. Commit: `git add CHANGELOG.md && git commit -m "Update CHANGELOG.md for v release"` +2. Push: `git push -u origin release/v` +3. Create a PR on GitHub targeting `main`: + - **With `gh` CLI:** `gh pr create --base main --title "Update CHANGELOG.md for v release" --body "Release changelog for v"` +4. Get the PR reviewed and merged + +### Step 4: Create Git Tag + +After the changelog PR is merged: + +1. Pull latest main: +```bash +git switch main +git pull origin main +``` + +2. Create and push the tag: +```bash +git tag -a v -m "Release v" +git push origin v +``` + +> **Important:** The tag must be on the `main` commit that includes the changelog update. + +### Step 5: Publish to PyPI + +Trigger the Azure DevOps CI/CD pipeline: +- Pipeline: https://dev.azure.com/dynamicscrm/OneCRM/_build?definitionId=29949 + +**Runtime variables (set when queuing the pipeline):** + +| Variable | Description | +|---|---| +| `PushToPyPI` | Set to `true` to publish to PyPI. If not set, the pipeline builds, tests, and produces artifacts without publishing. | +| `PackageVersion` | The version string for the release (e.g., `0.1.0b7`). Leave empty to use the version from `pyproject.toml`. | + +**Recommendation:** First run the pipeline **without** setting `PushToPyPI` to `true`. This validates the build, tests, and packaging. Once the dry run succeeds, queue again with `PushToPyPI` set to `true` to publish. + +Verify the package appears on PyPI: https://pypi.org/project/PowerPlatform-Dataverse-Client/ + +### Step 6: Create GitHub Release + +**Before writing release notes:** Review previous releases at https://github.com/microsoft/PowerPlatform-DataverseClient-Python/releases to match the tone, detail level, and formatting conventions. + +- **With `gh` CLI:** Extract the release notes from CHANGELOG.md (the Added/Fixed/Changed sections for this version) into a temp file, then run: + ```bash + gh release create v --title "v" --notes-file --prerelease + ``` + Omit `--prerelease` if the version does **not** contain `a`, `b`, or `rc`. +- **Without `gh` CLI:** + 1. Go to: https://github.com/microsoft/PowerPlatform-DataverseClient-Python/releases/new + 2. Select the tag: `v` + 3. Title: `v` + 4. Copy release notes from CHANGELOG.md + 5. Check **"Set as a pre-release"** if the version contains `a`, `b`, or `rc` (alpha/beta/release candidate) + 6. Click **Publish release** + +### Step 7: Post-Release Version Bump + +Immediately after the release, bump the version for the next development cycle: + +1. Create a branch: `git checkout -b post-release/bump-` +2. Update `version` in `pyproject.toml` to the next beta (e.g., `0.1.0b6` → `0.1.0b7`) +3. Stage and commit: +```bash +git add pyproject.toml +git commit -m "Bump version to for next development cycle" +``` +4. Push: `git push -u origin post-release/bump-` +5. Create a PR on GitHub and merge it: + - **With `gh` CLI:** `gh pr create --base main --title "Bump version to for next development cycle" --body "Post-release version bump"` + +--- + +## Version Numbering + +This project uses Semantic Versioning with PEP 440 pre-release identifiers: +- Beta releases: `0.1.0b1`, `0.1.0b2`, `0.1.0b3`, ... +- The version in `pyproject.toml` on `main` should always be one ahead of the latest published release + +--- + +## Key Links + +| Resource | URL | +|---|---| +| Repository | https://github.com/microsoft/PowerPlatform-DataverseClient-Python | +| PyPI Package | https://pypi.org/project/PowerPlatform-Dataverse-Client/ | +| CI/CD Pipeline | https://dev.azure.com/dynamicscrm/OneCRM/_build?definitionId=29949 | +| Releases | https://github.com/microsoft/PowerPlatform-DataverseClient-Python/releases | +| Contributing Guide | https://github.com/microsoft/PowerPlatform-DataverseClient-Python/blob/main/CONTRIBUTING.md | \ No newline at end of file diff --git a/.ipynb_checkpoints/async_jupyter_demo-checkpoint.ipynb b/.ipynb_checkpoints/async_jupyter_demo-checkpoint.ipynb new file mode 100644 index 00000000..b9f83332 --- /dev/null +++ b/.ipynb_checkpoints/async_jupyter_demo-checkpoint.ipynb @@ -0,0 +1,104 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Async-only SDK in Jupyter — what sync users experience\n", + "\n", + "Jupyter runs all cells inside a **persistent event loop**. This notebook shows what happens when a sync user tries to use an async-only SDK." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import asyncio\n", + "\n", + "# Simulated async-only SDK method (no sync client exists)\n", + "async def sdk_create_record(table: str, data: dict) -> str:\n", + " await asyncio.sleep(0) # yields control, like a real network call\n", + " return f\"fake-guid-for-{data.get('name', 'unknown')}\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Attempt 1 — call without `await` (forgetting to await)\n", + "\n", + "A user unfamiliar with async might call the function directly." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "result = sdk_create_record(\"account\", {\"name\": \"Contoso\"})\n", + "print(f\"Return value: {result}\")\n", + "print(f\"Type: {type(result)}\")\n", + "print()\n", + "print(\"The coroutine was never executed — no network call happened.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Attempt 2 — use `asyncio.run()` (the \"sync\" workaround)\n", + "\n", + "A sync user who learns about `asyncio.run()` tries to use it to avoid writing `async def`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# This is what a sync user would try after reading the docs\n", + "record_id = asyncio.run(sdk_create_record(\"account\", {\"name\": \"Contoso\"}))\n", + "print(f\"Created: {record_id}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Attempt 3 — use `await` (correct, but requires `async def`)\n", + "\n", + "The only way to call the SDK in Jupyter is with `await` — which works because Jupyter supports top-level `await`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# This works — Jupyter supports top-level await\n", + "record_id = await sdk_create_record(\"account\", {\"name\": \"Contoso\"})\n", + "print(f\"Created: {record_id}\")\n", + "print()\n", + "print(\"Works — but the user must learn async/await just to use the SDK.\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "name": "python", + "version": "3.12.0" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/_check_exports.py b/_check_exports.py new file mode 100644 index 00000000..0b2ffb28 --- /dev/null +++ b/_check_exports.py @@ -0,0 +1,51 @@ +"""Temporary script to compare module __all__ vs package __init__.py re-exports.""" +import importlib + +packages = { + "models": [ + "PowerPlatform.Dataverse.models.batch", + "PowerPlatform.Dataverse.models.fetchxml_query", + "PowerPlatform.Dataverse.models.filters", + "PowerPlatform.Dataverse.models.labels", + "PowerPlatform.Dataverse.models.protocol", + "PowerPlatform.Dataverse.models.query_builder", + "PowerPlatform.Dataverse.models.record", + "PowerPlatform.Dataverse.models.relationship", + "PowerPlatform.Dataverse.models.table_info", + "PowerPlatform.Dataverse.models.upsert", + ], + "core": [ + "PowerPlatform.Dataverse.core.config", + "PowerPlatform.Dataverse.core.errors", + "PowerPlatform.Dataverse.core.log_config", + ], + "operations": [ + "PowerPlatform.Dataverse.operations.batch", + "PowerPlatform.Dataverse.operations.dataframe", + "PowerPlatform.Dataverse.operations.files", + "PowerPlatform.Dataverse.operations.query", + "PowerPlatform.Dataverse.operations.records", + "PowerPlatform.Dataverse.operations.tables", + ], +} + +for pkg_name, modules in packages.items(): + pkg = importlib.import_module("PowerPlatform.Dataverse." + pkg_name) + pkg_all = set(pkg.__all__) + all_module_exports = set() + + for mod_name in modules: + mod = importlib.import_module(mod_name) + mod_all = set(getattr(mod, "__all__", [])) + all_module_exports |= mod_all + missing = mod_all - pkg_all + if missing: + short = mod_name.split(".")[-1] + print("MISSING from " + pkg_name + "/__init__.py (in " + short + "): " + str(sorted(missing))) + + extra = pkg_all - all_module_exports + if extra: + print("EXTRA in " + pkg_name + "/__init__.py: " + str(sorted(extra))) + + if not (all_module_exports - pkg_all) and not extra: + print(pkg_name + ": All exports match perfectly") diff --git a/_check_imports.py b/_check_imports.py new file mode 100644 index 00000000..76a5d10c --- /dev/null +++ b/_check_imports.py @@ -0,0 +1,44 @@ +"""Check that all example scripts can import their dependencies.""" +import ast +import importlib +import sys +import os + +EXAMPLES = [ + "examples/basic/installation_example.py", + "examples/basic/functional_testing.py", + "examples/advanced/walkthrough.py", + "examples/advanced/sql_examples.py", + "examples/advanced/dataframe_operations.py", + "examples/advanced/batch.py", + "examples/advanced/relationships.py", + "examples/advanced/fetchxml.py", + "examples/advanced/alternate_keys_upsert.py", + "examples/advanced/file_upload.py", + "examples/advanced/prodev_quick_start.py", + "examples/advanced/datascience_risk_assessment.py", +] + +failures = [] +for path in EXAMPLES: + name = os.path.basename(path) + try: + with open(path) as f: + tree = ast.parse(f.read()) + for node in ast.walk(tree): + if isinstance(node, ast.ImportFrom) and node.module and node.level == 0: + importlib.import_module(node.module) + elif isinstance(node, ast.Import): + for alias in node.names: + importlib.import_module(alias.name) + print(f"[OK] {name}") + except Exception as e: + print(f"[ERR] {name}: {e}") + failures.append(name) + +print() +if failures: + print(f"FAILURES: {failures}") + sys.exit(1) +else: + print("All 12 examples import successfully.") diff --git a/_tee_run.py b/_tee_run.py new file mode 100644 index 00000000..28dfa340 --- /dev/null +++ b/_tee_run.py @@ -0,0 +1,41 @@ +"""Wrapper that tees stdout/stderr to a log file while preserving interactive TTY.""" +import sys +import os +import io + + +class _Tee: + def __init__(self, original, log_file): + self._original = original + self._log = log_file + + def write(self, data): + self._original.write(data) + self._original.flush() + self._log.write(data) + self._log.flush() + + def flush(self): + self._original.flush() + self._log.flush() + + def __getattr__(self, name): + return getattr(self._original, name) + + +if __name__ == "__main__": + if len(sys.argv) < 3: + print(f"Usage: {sys.argv[0]} [args...]") + sys.exit(1) + + log_path = sys.argv[1] + script = sys.argv[2] + sys.argv = sys.argv[2:] # make the target script see its own argv + + with open(log_path, "w", encoding="utf-8") as log_file: + sys.stdout = _Tee(sys.__stdout__, log_file) + sys.stderr = _Tee(sys.__stderr__, log_file) + + with open(script) as f: + code = compile(f.read(), script, "exec") + exec(code, {"__name__": "__main__", "__file__": script}) diff --git a/async_jupyter_demo.ipynb b/async_jupyter_demo.ipynb new file mode 100644 index 00000000..1a3f9f8b --- /dev/null +++ b/async_jupyter_demo.ipynb @@ -0,0 +1,120 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "bae4ba8a", + "metadata": {}, + "source": [ + "## Async-only SDK in Jupyter — what sync users experience\n", + "\n", + "Jupyter runs all cells inside a **persistent event loop**. This notebook shows what happens when a sync user tries to use an async-only SDK." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ca683481", + "metadata": {}, + "outputs": [], + "source": [ + "import asyncio\n", + "\n", + "# Simulated async-only SDK method (no sync client exists)\n", + "async def sdk_create_record(table: str, data: dict) -> str:\n", + " await asyncio.sleep(0) # yields control, like a real network call\n", + " return f\"fake-guid-for-{data.get('name', 'unknown')}\"" + ] + }, + { + "cell_type": "markdown", + "id": "44b76928", + "metadata": {}, + "source": [ + "### Attempt 1 — call without `await` (forgetting to await)\n", + "\n", + "A user unfamiliar with async might call the function directly." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ef36bec7", + "metadata": {}, + "outputs": [], + "source": [ + "result = sdk_create_record(\"account\", {\"name\": \"Contoso\"})\n", + "print(f\"Return value: {result}\")\n", + "print(f\"Type: {type(result)}\")\n", + "print()\n", + "print(\"The coroutine was never executed — no network call happened.\")" + ] + }, + { + "cell_type": "markdown", + "id": "a69ac429", + "metadata": {}, + "source": [ + "### Attempt 2 — use `asyncio.run()` (the \"sync\" workaround)\n", + "\n", + "A sync user who learns about `asyncio.run()` tries to use it to avoid writing `async def`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "731964bd", + "metadata": {}, + "outputs": [], + "source": [ + "# This is what a sync user would try after reading the docs\n", + "record_id = asyncio.run(sdk_create_record(\"account\", {\"name\": \"Contoso\"}))\n", + "print(f\"Created: {record_id}\")" + ] + }, + { + "cell_type": "markdown", + "id": "ba7777dd", + "metadata": {}, + "source": [ + "### Attempt 3 — use `await` (correct, but requires `async def`)\n", + "\n", + "The only way to call the SDK in Jupyter is with `await` — which works because Jupyter supports top-level `await`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b44f7309", + "metadata": {}, + "outputs": [], + "source": [ + "# This works — Jupyter supports top-level await\n", + "record_id = await sdk_create_record(\"account\", {\"name\": \"Contoso\"})\n", + "print(f\"Created: {record_id}\")\n", + "print()\n", + "print(\"Works — but the user must learn async/await just to use the SDK.\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.9" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/coverage.json b/coverage.json new file mode 100644 index 00000000..b5edbb9b --- /dev/null +++ b/coverage.json @@ -0,0 +1 @@ +{"meta": {"format": 3, "version": "7.13.5", "timestamp": "2026-04-27T21:59:36.005769", "branch_coverage": false, "show_contexts": false}, "files": {"src\\PowerPlatform\\Dataverse\\__init__.py": {"executed_lines": [4, 6, 8], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [4, 6, 8], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [4, 6, 8], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\_skill_installer.py": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 139, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 139, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [13, 14, 15, 18, 24, 28, 29, 31, 34, 35, 36, 40, 41, 42, 44, 47, 49, 50, 56, 66, 67, 70, 71, 73, 74, 77, 78, 79, 80, 81, 83, 84, 85, 86, 87, 88, 91, 92, 93, 94, 95, 96, 97, 98, 101, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 129, 132, 139, 141, 142, 144, 145, 146, 147, 149, 150, 151, 152, 153, 154, 155, 156, 158, 159, 160, 162, 163, 164, 167, 169, 171, 172, 174, 175, 176, 177, 179, 180, 181, 182, 183, 184, 185, 187, 188, 190, 191, 193, 194, 195, 196, 197, 198, 199, 201, 202, 205, 207, 209, 238, 242, 244, 246, 248, 249, 250, 251, 254, 255, 256, 258, 259, 260, 263, 264, 267, 268], "excluded_lines": [], "functions": {"get_skill_source_paths": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 11, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 11, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [24, 28, 29, 31, 34, 35, 36, 40, 41, 42, 44], "excluded_lines": [], "start_line": 18}, "get_skill_destination_paths": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [49, 50], "excluded_lines": [], "start_line": 47}, "install_skill": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 49, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 49, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [66, 67, 70, 71, 73, 74, 77, 78, 79, 80, 81, 83, 84, 85, 86, 87, 88, 91, 92, 93, 94, 95, 96, 97, 98, 101, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 129], "excluded_lines": [], "start_line": 56}, "uninstall_skill": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 21, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 21, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [139, 141, 142, 144, 145, 146, 147, 149, 150, 151, 152, 153, 154, 155, 156, 158, 159, 160, 162, 163, 164], "excluded_lines": [], "start_line": 132}, "check_skill_status": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 27, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 27, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [169, 171, 172, 174, 175, 176, 177, 179, 180, 181, 182, 183, 184, 185, 187, 188, 190, 191, 193, 194, 195, 196, 197, 198, 199, 201, 202], "excluded_lines": [], "start_line": 167}, "main": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 18, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 18, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [207, 209, 238, 242, 244, 246, 248, 249, 250, 251, 254, 255, 256, 258, 259, 260, 263, 264], "excluded_lines": [], "start_line": 205}, "": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 11, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 11, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [13, 14, 15, 18, 47, 56, 132, 167, 205, 267, 268], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 139, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 139, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [13, 14, 15, 18, 24, 28, 29, 31, 34, 35, 36, 40, 41, 42, 44, 47, 49, 50, 56, 66, 67, 70, 71, 73, 74, 77, 78, 79, 80, 81, 83, 84, 85, 86, 87, 88, 91, 92, 93, 94, 95, 96, 97, 98, 101, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 129, 132, 139, 141, 142, 144, 145, 146, 147, 149, 150, 151, 152, 153, 154, 155, 156, 158, 159, 160, 162, 163, 164, 167, 169, 171, 172, 174, 175, 176, 177, 179, 180, 181, 182, 183, 184, 185, 187, 188, 190, 191, 193, 194, 195, 196, 197, 198, 199, 201, 202, 205, 207, 209, 238, 242, 244, 246, 248, 249, 250, 251, 254, 255, 256, 258, 259, 260, 263, 264, 267, 268], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\__init__.py": {"executed_lines": [4, 6], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [4, 6], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [4, 6], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\async_client.py": {"executed_lines": [4, 6, 7, 9, 10, 12, 13, 14, 15, 16, 17, 18, 19, 20, 23, 93, 99, 100, 101, 102, 103, 104, 105, 106, 109, 110, 111, 112, 113, 114, 116, 126, 127, 133, 135, 136, 138, 139, 141, 142, 146, 158, 159, 160, 161, 163, 169, 171, 188, 189, 190, 191, 192, 193, 194, 195, 196, 198, 200, 201], "summary": {"covered_lines": 60, "num_statements": 60, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"AsyncDataverseClient.__init__": {"executed_lines": [99, 100, 101, 102, 103, 104, 105, 106, 109, 110, 111, 112, 113, 114], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 93}, "AsyncDataverseClient._get_odata": {"executed_lines": [126, 127, 133], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 116}, "AsyncDataverseClient._scoped_odata": {"executed_lines": [138, 139, 141, 142], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 136}, "AsyncDataverseClient.__aenter__": {"executed_lines": [158, 159, 160, 161], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 146}, "AsyncDataverseClient.__aexit__": {"executed_lines": [169], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 163}, "AsyncDataverseClient.aclose": {"executed_lines": [188, 189, 190, 191, 192, 193, 194, 195, 196], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 171}, "AsyncDataverseClient._check_closed": {"executed_lines": [200, 201], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 198}, "": {"executed_lines": [4, 6, 7, 9, 10, 12, 13, 14, 15, 16, 17, 18, 19, 20, 23, 93, 116, 135, 136, 146, 163, 171, 198], "summary": {"covered_lines": 23, "num_statements": 23, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"AsyncDataverseClient": {"executed_lines": [99, 100, 101, 102, 103, 104, 105, 106, 109, 110, 111, 112, 113, 114, 126, 127, 133, 138, 139, 141, 142, 158, 159, 160, 161, 169, 188, 189, 190, 191, 192, 193, 194, 195, 196, 200, 201], "summary": {"covered_lines": 37, "num_statements": 37, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 23}, "": {"executed_lines": [4, 6, 7, 9, 10, 12, 13, 14, 15, 16, 17, 18, 19, 20, 23, 93, 116, 135, 136, 146, 163, 171, 198], "summary": {"covered_lines": 23, "num_statements": 23, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\core\\__init__.py": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\core\\_async_auth.py": {"executed_lines": [13, 15, 17, 20, 29, 30, 31, 32, 34, 44, 45], "summary": {"covered_lines": 11, "num_statements": 11, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"_AsyncAuthManager.__init__": {"executed_lines": [30, 31, 32], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 29}, "_AsyncAuthManager._acquire_token": {"executed_lines": [44, 45], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 34}, "": {"executed_lines": [13, 15, 17, 20, 29, 34], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_AsyncAuthManager": {"executed_lines": [30, 31, 32, 44, 45], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 20}, "": {"executed_lines": [13, 15, 17, 20, 29, 34], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\core\\_async_http.py": {"executed_lines": [12, 14, 15, 16, 18, 24, 47, 55, 56, 57, 58, 59, 61, 82, 83, 87, 88, 89, 91, 92, 93, 97, 98, 100, 103, 104, 112, 113, 114, 115, 118, 119, 121, 124, 125, 133, 134, 135, 136, 143, 144, 145, 146, 147, 149, 154, 155, 156], "summary": {"covered_lines": 48, "num_statements": 50, "percent_covered": 96.0, "percent_covered_display": "96", "missing_lines": 2, "excluded_lines": 2, "percent_statements_covered": 96.0, "percent_statements_covered_display": "96"}, "missing_lines": [99, 101], "excluded_lines": [20, 21], "functions": {"_AsyncHttpClient.__init__": {"executed_lines": [55, 56, 57, 58, 59], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 47}, "_AsyncHttpClient._request": {"executed_lines": [82, 83, 87, 88, 89, 91, 92, 93, 97, 98, 100, 103, 104, 112, 113, 114, 115, 118, 119, 121, 124, 125, 133, 134, 135, 136, 143, 144, 145, 146, 147], "summary": {"covered_lines": 31, "num_statements": 33, "percent_covered": 93.93939393939394, "percent_covered_display": "94", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 93.93939393939394, "percent_statements_covered_display": "94"}, "missing_lines": [99, 101], "excluded_lines": [], "start_line": 61}, "_AsyncHttpClient.close": {"executed_lines": [154, 155, 156], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 149}, "": {"executed_lines": [12, 14, 15, 16, 18, 24, 47, 61, 149], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [20, 21], "start_line": 1}}, "classes": {"_AsyncHttpClient": {"executed_lines": [55, 56, 57, 58, 59, 82, 83, 87, 88, 89, 91, 92, 93, 97, 98, 100, 103, 104, 112, 113, 114, 115, 118, 119, 121, 124, 125, 133, 134, 135, 136, 143, 144, 145, 146, 147, 154, 155, 156], "summary": {"covered_lines": 39, "num_statements": 41, "percent_covered": 95.1219512195122, "percent_covered_display": "95", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 95.1219512195122, "percent_statements_covered_display": "95"}, "missing_lines": [99, 101], "excluded_lines": [], "start_line": 24}, "": {"executed_lines": [12, 14, 15, 16, 18, 24, 47, 61, 149], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [20, 21], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\data\\__init__.py": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\data\\_async_batch.py": {"executed_lines": [6, 8, 9, 11, 12, 13, 14, 15, 42, 45, 61, 62, 63, 64, 65, 67, 76, 90, 101, 102, 104, 106, 107, 115, 116, 118, 121, 122, 124, 125, 140, 141, 142, 146, 152, 158, 159, 160, 161, 162, 164, 165, 166, 167, 168, 170, 171, 173, 175, 176, 177, 179, 180, 181, 182, 183, 185, 187, 189, 191, 193, 195, 197, 199, 201, 203, 205, 207, 209, 214, 216, 217, 218, 222, 228, 229, 230, 231, 232, 234, 235, 236, 237, 238, 239, 240, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 255, 256, 258, 259, 260, 261, 262, 263, 264, 265, 273, 275, 276, 277, 281, 283, 284, 285, 287, 288, 289, 291, 292, 293, 294, 295, 296, 299, 300, 304, 305, 311, 312], "summary": {"covered_lines": 131, "num_statements": 149, "percent_covered": 87.91946308724832, "percent_covered_display": "88", "missing_lines": 18, "excluded_lines": 2, "percent_statements_covered": 87.91946308724832, "percent_statements_covered_display": "88"}, "missing_lines": [68, 108, 143, 144, 178, 184, 186, 188, 190, 192, 194, 196, 198, 200, 202, 204, 206, 208], "excluded_lines": [39, 40], "functions": {"_SyncResponseWrapper.__init__": {"executed_lines": [62, 63, 64, 65], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 61}, "_SyncResponseWrapper.json": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [68], "excluded_lines": [], "start_line": 67}, "_AsyncBatchClient.execute": {"executed_lines": [101, 102, 104, 106, 107, 115, 116, 118, 121, 122, 124, 125, 140, 141, 142, 146, 152], "summary": {"covered_lines": 17, "num_statements": 20, "percent_covered": 85.0, "percent_covered_display": "85", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 85.0, "percent_statements_covered_display": "85"}, "missing_lines": [108, 143, 144], "excluded_lines": [], "start_line": 90}, "_AsyncBatchClient._resolve_all": {"executed_lines": [159, 160, 161, 162, 164, 165, 166, 167, 168, 170, 171], "summary": {"covered_lines": 11, "num_statements": 11, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 158}, "_AsyncBatchClient._resolve_item": {"executed_lines": [175, 176, 177, 179, 180, 181, 182, 183, 185, 187, 189, 191, 193, 195, 197, 199, 201, 203, 205, 207, 209], "summary": {"covered_lines": 21, "num_statements": 35, "percent_covered": 60.0, "percent_covered_display": "60", "missing_lines": 14, "excluded_lines": 0, "percent_statements_covered": 60.0, "percent_statements_covered_display": "60"}, "missing_lines": [178, 184, 186, 188, 190, 192, 194, 196, 198, 200, 202, 204, 206, 208], "excluded_lines": [], "start_line": 173}, "_AsyncBatchClient._resolve_one": {"executed_lines": [216, 217, 218, 222], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 214}, "_AsyncBatchClient._resolve_record_create": {"executed_lines": [229, 230, 231, 232], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 228}, "_AsyncBatchClient._resolve_record_update": {"executed_lines": [235, 236, 237, 238, 239, 240], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 234}, "_AsyncBatchClient._resolve_record_delete": {"executed_lines": [243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253], "summary": {"covered_lines": 11, "num_statements": 11, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 242}, "_AsyncBatchClient._resolve_record_get": {"executed_lines": [256], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 255}, "_AsyncBatchClient._resolve_record_upsert": {"executed_lines": [259, 260, 261, 262, 263, 264, 265], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 258}, "_AsyncBatchClient._require_entity_metadata": {"executed_lines": [275, 276, 277, 281], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 273}, "_AsyncBatchClient._resolve_table_delete": {"executed_lines": [284, 285], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 283}, "_AsyncBatchClient._resolve_table_add_columns": {"executed_lines": [288, 289], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 287}, "_AsyncBatchClient._resolve_table_remove_columns": {"executed_lines": [292, 293, 294, 295, 296, 299, 300, 304, 305], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 291}, "_AsyncBatchClient._resolve_query_sql": {"executed_lines": [312], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 311}, "": {"executed_lines": [6, 8, 9, 11, 12, 13, 14, 15, 42, 45, 61, 67, 76, 90, 158, 173, 214, 228, 234, 242, 255, 258, 273, 283, 287, 291, 311], "summary": {"covered_lines": 27, "num_statements": 27, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [39, 40], "start_line": 1}}, "classes": {"_SyncResponseWrapper": {"executed_lines": [62, 63, 64, 65], "summary": {"covered_lines": 4, "num_statements": 5, "percent_covered": 80.0, "percent_covered_display": "80", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 80.0, "percent_statements_covered_display": "80"}, "missing_lines": [68], "excluded_lines": [], "start_line": 45}, "_AsyncBatchClient": {"executed_lines": [101, 102, 104, 106, 107, 115, 116, 118, 121, 122, 124, 125, 140, 141, 142, 146, 152, 159, 160, 161, 162, 164, 165, 166, 167, 168, 170, 171, 175, 176, 177, 179, 180, 181, 182, 183, 185, 187, 189, 191, 193, 195, 197, 199, 201, 203, 205, 207, 209, 216, 217, 218, 222, 229, 230, 231, 232, 235, 236, 237, 238, 239, 240, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 256, 259, 260, 261, 262, 263, 264, 265, 275, 276, 277, 281, 284, 285, 288, 289, 292, 293, 294, 295, 296, 299, 300, 304, 305, 312], "summary": {"covered_lines": 100, "num_statements": 117, "percent_covered": 85.47008547008546, "percent_covered_display": "85", "missing_lines": 17, "excluded_lines": 0, "percent_statements_covered": 85.47008547008546, "percent_statements_covered_display": "85"}, "missing_lines": [108, 143, 144, 178, 184, 186, 188, 190, 192, 194, 196, 198, 200, 202, 204, 206, 208], "excluded_lines": [], "start_line": 76}, "": {"executed_lines": [6, 8, 9, 11, 12, 13, 14, 15, 42, 45, 61, 67, 76, 90, 158, 173, 214, 228, 234, 242, 255, 258, 273, 283, 287, 291, 311], "summary": {"covered_lines": 27, "num_statements": 27, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [39, 40], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\data\\_async_odata.py": {"executed_lines": [6, 8, 10, 11, 12, 13, 14, 15, 17, 19, 21, 22, 23, 24, 25, 26, 39, 49, 52, 73, 74, 75, 83, 89, 90, 91, 93, 95, 96, 97, 106, 109, 120, 122, 123, 124, 129, 137, 138, 139, 141, 142, 143, 144, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 160, 161, 162, 163, 164, 165, 166, 167, 172, 173, 174, 175, 176, 177, 180, 181, 199, 210, 211, 212, 213, 214, 215, 218, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 250, 271, 272, 273, 274, 275, 278, 281, 282, 283, 285, 286, 288, 289, 290, 292, 293, 294, 295, 296, 297, 299, 325, 326, 327, 328, 329, 331, 362, 363, 367, 368, 369, 370, 371, 372, 373, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 388, 390, 391, 392, 393, 403, 421, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 439, 440, 441, 443, 458, 459, 460, 461, 465, 466, 467, 470, 471, 472, 474, 486, 488, 511, 512, 513, 514, 516, 527, 529, 547, 548, 550, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 597, 598, 599, 600, 601, 605, 606, 607, 608, 610, 611, 613, 614, 616, 617, 619, 620, 621, 622, 623, 625, 626, 627, 628, 630, 631, 632, 634, 635, 636, 637, 638, 639, 642, 661, 662, 663, 664, 665, 670, 672, 673, 674, 679, 680, 681, 682, 685, 686, 687, 690, 691, 692, 693, 694, 696, 697, 706, 707, 711, 712, 725, 726, 736, 737, 747, 748, 749, 750, 752, 753, 754, 756, 759, 764, 765, 768, 769, 770, 771, 772, 774, 775, 776, 780, 781, 782, 783, 786, 787, 792, 796, 797, 798, 799, 803, 804, 805, 806, 807, 810, 821, 823, 824, 825, 829, 830, 831, 833, 840, 841, 853, 854, 856, 857, 861, 865, 867, 869, 876, 877, 878, 879, 880, 881, 882, 883, 885, 886, 887, 888, 889, 893, 894, 895, 898, 899, 900, 901, 902, 903, 905, 933, 934, 935, 939, 940, 941, 942, 943, 944, 945, 946, 947, 949, 962, 963, 964, 965, 967, 968, 970, 971, 972, 973, 974, 975, 978, 983, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 998, 1006, 1007, 1008, 1009, 1010, 1012, 1013, 1018, 1019, 1020, 1022, 1023, 1024, 1026, 1027, 1029, 1030, 1031, 1032, 1033, 1034, 1036, 1037, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1050, 1052, 1061, 1064, 1068, 1069, 1072, 1075, 1076, 1077, 1079, 1081, 1082, 1083, 1084, 1086, 1087, 1088, 1089, 1090, 1091, 1092, 1093, 1094, 1096, 1105, 1106, 1107, 1108, 1118, 1145, 1146, 1148, 1160, 1161, 1162, 1166, 1170, 1197, 1198, 1199, 1204, 1205, 1206, 1210, 1212, 1213, 1215, 1221, 1235, 1236, 1237, 1242, 1243, 1244, 1245, 1247, 1263, 1264, 1265, 1270, 1271, 1272, 1274, 1304, 1305, 1306, 1311, 1315, 1316, 1318, 1322, 1323, 1324, 1325, 1326, 1327, 1328, 1329, 1331, 1332, 1333, 1334, 1335, 1337, 1338, 1339, 1341, 1348, 1358, 1378, 1379, 1381, 1382, 1383, 1388, 1389, 1390, 1392, 1393, 1394, 1395, 1399, 1400, 1401, 1406, 1407, 1409, 1410, 1412, 1414, 1435, 1436, 1437, 1438, 1440, 1442, 1443, 1444, 1446, 1447, 1448, 1454, 1455, 1456, 1457, 1459, 1460, 1461, 1462, 1467, 1468, 1469, 1471, 1473, 1474, 1475, 1476, 1477, 1479, 1481, 1482, 1484, 1488, 1497, 1498, 1499, 1506, 1513, 1515, 1516, 1517, 1518, 1519, 1520, 1521, 1522, 1523, 1529, 1542, 1543, 1544, 1545, 1547, 1548, 1549, 1557, 1569, 1570, 1571, 1572, 1573, 1574, 1575, 1576, 1577, 1583, 1591, 1592, 1593, 1594, 1595, 1596, 1600, 1602, 1603, 1605, 1617, 1618, 1619, 1620, 1621, 1627, 1635, 1636, 1641, 1642, 1643, 1644, 1645, 1646, 1647, 1650, 1651, 1655, 1656, 1657, 1658, 1659, 1660, 1666, 1677, 1678, 1680, 1681, 1682, 1689, 1691, 1692, 1693, 1694, 1725, 1731, 1739, 1740, 1741, 1742, 1743, 1745, 1760, 1761, 1762], "summary": {"covered_lines": 646, "num_statements": 699, "percent_covered": 92.41773962804005, "percent_covered_display": "92", "missing_lines": 53, "excluded_lines": 0, "percent_statements_covered": 92.41773962804005, "percent_statements_covered_display": "92"}, "missing_lines": [107, 125, 126, 127, 145, 146, 178, 179, 276, 277, 279, 395, 396, 397, 398, 399, 422, 438, 468, 469, 602, 603, 675, 676, 683, 713, 714, 723, 724, 727, 728, 735, 738, 739, 746, 751, 784, 785, 855, 862, 866, 884, 896, 897, 969, 1025, 1028, 1035, 1038, 1078, 1085, 1211, 1514], "excluded_lines": [], "functions": {"_AsyncODataClient.__init__": {"executed_lines": [73, 74, 75], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 52}, "_AsyncODataClient.close": {"executed_lines": [89, 90, 91], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 83}, "_AsyncODataClient._headers": {"executed_lines": [95, 96, 97], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 93}, "_AsyncODataClient._raw_request": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [107], "excluded_lines": [], "start_line": 106}, "_AsyncODataClient._request": {"executed_lines": [120, 122, 129, 137, 138, 139, 141, 142, 143, 144, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 160, 161, 162, 163, 164, 165, 166, 167, 172, 173, 174, 175, 176, 177, 180, 181], "summary": {"covered_lines": 38, "num_statements": 42, "percent_covered": 90.47619047619048, "percent_covered_display": "90", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 90.47619047619048, "percent_statements_covered_display": "90"}, "missing_lines": [145, 146, 178, 179], "excluded_lines": [], "start_line": 109}, "_AsyncODataClient._request._merge": {"executed_lines": [123, 124], "summary": {"covered_lines": 2, "num_statements": 5, "percent_covered": 40.0, "percent_covered_display": "40", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 40.0, "percent_statements_covered_display": "40"}, "missing_lines": [125, 126, 127], "excluded_lines": [], "start_line": 122}, "_AsyncODataClient._execute_raw": {"executed_lines": [210, 211, 212, 213, 214, 215], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 199}, "_AsyncODataClient._create": {"executed_lines": [234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 218}, "_AsyncODataClient._create_multiple": {"executed_lines": [271, 272, 273, 274, 275, 278, 281, 282, 283, 285, 286, 288, 289, 290, 292, 293, 294, 295, 296, 297], "summary": {"covered_lines": 20, "num_statements": 23, "percent_covered": 86.95652173913044, "percent_covered_display": "87", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 86.95652173913044, "percent_statements_covered_display": "87"}, "missing_lines": [276, 277, 279], "excluded_lines": [], "start_line": 250}, "_AsyncODataClient._upsert": {"executed_lines": [325, 326, 327, 328, 329], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 299}, "_AsyncODataClient._upsert_multiple": {"executed_lines": [362, 363, 367, 368, 369, 370, 371, 372, 373, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385], "summary": {"covered_lines": 19, "num_statements": 19, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 331}, "_AsyncODataClient._primary_id_attr": {"executed_lines": [390, 391, 392, 393], "summary": {"covered_lines": 4, "num_statements": 9, "percent_covered": 44.44444444444444, "percent_covered_display": "44", "missing_lines": 5, "excluded_lines": 0, "percent_statements_covered": 44.44444444444444, "percent_statements_covered_display": "44"}, "missing_lines": [395, 396, 397, 398, 399], "excluded_lines": [], "start_line": 388}, "_AsyncODataClient._update_by_ids": {"executed_lines": [421, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 439, 440, 441], "summary": {"covered_lines": 19, "num_statements": 21, "percent_covered": 90.47619047619048, "percent_covered_display": "90", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 90.47619047619048, "percent_statements_covered_display": "90"}, "missing_lines": [422, 438], "excluded_lines": [], "start_line": 403}, "_AsyncODataClient._delete_multiple": {"executed_lines": [458, 459, 460, 461, 465, 466, 467, 470, 471, 472], "summary": {"covered_lines": 10, "num_statements": 12, "percent_covered": 83.33333333333333, "percent_covered_display": "83", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 83.33333333333333, "percent_statements_covered_display": "83"}, "missing_lines": [468, 469], "excluded_lines": [], "start_line": 443}, "_AsyncODataClient._update": {"executed_lines": [486], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 474}, "_AsyncODataClient._update_multiple": {"executed_lines": [511, 512, 513, 514], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 488}, "_AsyncODataClient._delete": {"executed_lines": [527], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 516}, "_AsyncODataClient._get": {"executed_lines": [547, 548], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 529}, "_AsyncODataClient._get_multiple": {"executed_lines": [586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 597, 605, 606, 607, 608, 610, 611, 613, 614, 616, 617, 619, 620, 621, 622, 623, 625, 626, 627, 628, 630, 631, 632, 634, 635, 636, 637, 638, 639], "summary": {"covered_lines": 39, "num_statements": 39, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 550}, "_AsyncODataClient._get_multiple._do_request": {"executed_lines": [598, 599, 600, 601], "summary": {"covered_lines": 4, "num_statements": 6, "percent_covered": 66.66666666666667, "percent_covered_display": "67", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 66.66666666666667, "percent_statements_covered_display": "67"}, "missing_lines": [602, 603], "excluded_lines": [], "start_line": 597}, "_AsyncODataClient._query_sql": {"executed_lines": [661, 662, 663, 664, 665, 670, 672, 673, 674, 679, 680, 681, 682, 685, 686, 687, 690, 691, 692, 693, 694, 696, 697, 706, 707, 711, 712, 725, 726, 736, 737, 747, 748, 749, 750, 752, 753, 754, 756], "summary": {"covered_lines": 39, "num_statements": 53, "percent_covered": 73.58490566037736, "percent_covered_display": "74", "missing_lines": 14, "excluded_lines": 0, "percent_statements_covered": 73.58490566037736, "percent_statements_covered_display": "74"}, "missing_lines": [675, 676, 683, 713, 714, 723, 724, 727, 728, 735, 738, 739, 746, 751], "excluded_lines": [], "start_line": 642}, "_AsyncODataClient._entity_set_from_schema_name": {"executed_lines": [764, 765, 768, 769, 770, 771, 772, 774, 775, 776, 780, 781, 782, 783, 786, 787, 792, 796, 797, 798, 799, 803, 804, 805, 806, 807], "summary": {"covered_lines": 26, "num_statements": 28, "percent_covered": 92.85714285714286, "percent_covered_display": "93", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 92.85714285714286, "percent_statements_covered_display": "93"}, "missing_lines": [784, 785], "excluded_lines": [], "start_line": 759}, "_AsyncODataClient._get_entity_by_table_schema_name": {"executed_lines": [821, 823, 824, 825, 829, 830, 831], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 810}, "_AsyncODataClient._create_entity": {"executed_lines": [840, 841, 853, 854, 856, 857, 861, 865, 867], "summary": {"covered_lines": 9, "num_statements": 12, "percent_covered": 75.0, "percent_covered_display": "75", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 75.0, "percent_statements_covered_display": "75"}, "missing_lines": [855, 862, 866], "excluded_lines": [], "start_line": 833}, "_AsyncODataClient._get_attribute_metadata": {"executed_lines": [876, 877, 878, 879, 880, 881, 882, 883, 885, 886, 887, 888, 889, 893, 894, 895, 898, 899, 900, 901, 902, 903], "summary": {"covered_lines": 22, "num_statements": 25, "percent_covered": 88.0, "percent_covered_display": "88", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 88.0, "percent_statements_covered_display": "88"}, "missing_lines": [884, 896, 897], "excluded_lines": [], "start_line": 869}, "_AsyncODataClient._list_columns": {"executed_lines": [933, 934, 935, 939, 940, 941, 942, 943, 944, 945, 946, 947], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 905}, "_AsyncODataClient._wait_for_attribute_visibility": {"executed_lines": [962, 963, 964, 965, 967, 968, 970, 971, 972, 973, 974, 975, 978], "summary": {"covered_lines": 13, "num_statements": 14, "percent_covered": 92.85714285714286, "percent_covered_display": "93", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 92.85714285714286, "percent_statements_covered_display": "93"}, "missing_lines": [969], "excluded_lines": [], "start_line": 949}, "_AsyncODataClient._request_metadata_with_retry": {"executed_lines": [985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 983}, "_AsyncODataClient._bulk_fetch_picklists": {"executed_lines": [1006, 1007, 1008, 1009, 1010, 1012, 1013, 1018, 1019, 1020, 1022, 1023, 1024, 1026, 1027, 1029, 1030, 1031, 1032, 1033, 1034, 1036, 1037, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1050], "summary": {"covered_lines": 34, "num_statements": 38, "percent_covered": 89.47368421052632, "percent_covered_display": "89", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 89.47368421052632, "percent_statements_covered_display": "89"}, "missing_lines": [1025, 1028, 1035, 1038], "excluded_lines": [], "start_line": 998}, "_AsyncODataClient._convert_labels_to_ints": {"executed_lines": [1061, 1064, 1068, 1069, 1072, 1075, 1076, 1077, 1079, 1081, 1082, 1083, 1084, 1086, 1087, 1088, 1089, 1090, 1091, 1092, 1093, 1094], "summary": {"covered_lines": 22, "num_statements": 24, "percent_covered": 91.66666666666667, "percent_covered_display": "92", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 91.66666666666667, "percent_statements_covered_display": "92"}, "missing_lines": [1078, 1085], "excluded_lines": [], "start_line": 1052}, "_AsyncODataClient._get_table_info": {"executed_lines": [1105, 1106, 1107, 1108], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1096}, "_AsyncODataClient._list_tables": {"executed_lines": [1145, 1146], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1118}, "_AsyncODataClient._delete_table": {"executed_lines": [1160, 1161, 1162, 1166], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1148}, "_AsyncODataClient._create_alternate_key": {"executed_lines": [1197, 1198, 1199, 1204, 1205, 1206, 1210, 1212, 1213, 1215], "summary": {"covered_lines": 10, "num_statements": 11, "percent_covered": 90.9090909090909, "percent_covered_display": "91", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 90.9090909090909, "percent_statements_covered_display": "91"}, "missing_lines": [1211], "excluded_lines": [], "start_line": 1170}, "_AsyncODataClient._get_alternate_keys": {"executed_lines": [1235, 1236, 1237, 1242, 1243, 1244, 1245], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1221}, "_AsyncODataClient._delete_alternate_key": {"executed_lines": [1263, 1264, 1265, 1270, 1271, 1272], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1247}, "_AsyncODataClient._create_table": {"executed_lines": [1304, 1305, 1306, 1311, 1315, 1316, 1318, 1322, 1323, 1324, 1325, 1326, 1327, 1328, 1329, 1331, 1332, 1333, 1334, 1335, 1337, 1338, 1339, 1341, 1348], "summary": {"covered_lines": 25, "num_statements": 25, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1274}, "_AsyncODataClient._create_columns": {"executed_lines": [1378, 1379, 1381, 1382, 1383, 1388, 1389, 1390, 1392, 1393, 1394, 1395, 1399, 1400, 1401, 1406, 1407, 1409, 1410, 1412], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1358}, "_AsyncODataClient._delete_columns": {"executed_lines": [1435, 1436, 1437, 1438, 1440, 1442, 1443, 1444, 1446, 1447, 1448, 1454, 1455, 1456, 1457, 1459, 1460, 1461, 1462, 1467, 1468, 1469, 1471, 1473, 1474, 1475, 1476, 1477, 1479, 1481, 1482, 1484], "summary": {"covered_lines": 32, "num_statements": 32, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1414}, "_AsyncODataClient._build_create": {"executed_lines": [1497, 1498, 1499], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1488}, "_AsyncODataClient._build_create_multiple": {"executed_lines": [1513, 1515, 1516, 1517, 1518, 1519, 1520, 1521, 1522, 1523], "summary": {"covered_lines": 10, "num_statements": 11, "percent_covered": 90.9090909090909, "percent_covered_display": "91", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 90.9090909090909, "percent_statements_covered_display": "91"}, "missing_lines": [1514], "excluded_lines": [], "start_line": 1506}, "_AsyncODataClient._build_update": {"executed_lines": [1542, 1543, 1544, 1545, 1547, 1548, 1549], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1529}, "_AsyncODataClient._build_update_multiple_from_records": {"executed_lines": [1569, 1570, 1571, 1572, 1573, 1574, 1575, 1576, 1577], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1557}, "_AsyncODataClient._build_update_multiple": {"executed_lines": [1591, 1592, 1593, 1594, 1595, 1596, 1600, 1602, 1603], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1583}, "_AsyncODataClient._build_upsert": {"executed_lines": [1617, 1618, 1619, 1620, 1621], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1605}, "_AsyncODataClient._build_upsert_multiple": {"executed_lines": [1635, 1636, 1641, 1642, 1643, 1644, 1645, 1646, 1647, 1650, 1651, 1655, 1656, 1657, 1658, 1659, 1660], "summary": {"covered_lines": 17, "num_statements": 17, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1627}, "_AsyncODataClient._build_delete": {"executed_lines": [1677, 1678, 1680, 1681, 1682], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1666}, "_AsyncODataClient._build_delete_multiple": {"executed_lines": [1691, 1692, 1693, 1694, 1725], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1689}, "_AsyncODataClient._build_get": {"executed_lines": [1739, 1740, 1741, 1742, 1743], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1731}, "_AsyncODataClient._build_sql": {"executed_lines": [1760, 1761, 1762], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1745}, "": {"executed_lines": [6, 8, 10, 11, 12, 13, 14, 15, 17, 19, 21, 22, 23, 24, 25, 26, 39, 49, 52, 83, 93, 106, 109, 199, 218, 250, 299, 331, 388, 403, 443, 474, 488, 516, 529, 550, 642, 759, 810, 833, 869, 905, 949, 983, 998, 1052, 1096, 1118, 1148, 1170, 1221, 1247, 1274, 1358, 1414, 1488, 1506, 1529, 1557, 1583, 1605, 1627, 1666, 1689, 1731, 1745], "summary": {"covered_lines": 66, "num_statements": 66, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_AsyncODataClient": {"executed_lines": [73, 74, 75, 89, 90, 91, 95, 96, 97, 120, 122, 123, 124, 129, 137, 138, 139, 141, 142, 143, 144, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 160, 161, 162, 163, 164, 165, 166, 167, 172, 173, 174, 175, 176, 177, 180, 181, 210, 211, 212, 213, 214, 215, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 271, 272, 273, 274, 275, 278, 281, 282, 283, 285, 286, 288, 289, 290, 292, 293, 294, 295, 296, 297, 325, 326, 327, 328, 329, 362, 363, 367, 368, 369, 370, 371, 372, 373, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 390, 391, 392, 393, 421, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 439, 440, 441, 458, 459, 460, 461, 465, 466, 467, 470, 471, 472, 486, 511, 512, 513, 514, 527, 547, 548, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 597, 598, 599, 600, 601, 605, 606, 607, 608, 610, 611, 613, 614, 616, 617, 619, 620, 621, 622, 623, 625, 626, 627, 628, 630, 631, 632, 634, 635, 636, 637, 638, 639, 661, 662, 663, 664, 665, 670, 672, 673, 674, 679, 680, 681, 682, 685, 686, 687, 690, 691, 692, 693, 694, 696, 697, 706, 707, 711, 712, 725, 726, 736, 737, 747, 748, 749, 750, 752, 753, 754, 756, 764, 765, 768, 769, 770, 771, 772, 774, 775, 776, 780, 781, 782, 783, 786, 787, 792, 796, 797, 798, 799, 803, 804, 805, 806, 807, 821, 823, 824, 825, 829, 830, 831, 840, 841, 853, 854, 856, 857, 861, 865, 867, 876, 877, 878, 879, 880, 881, 882, 883, 885, 886, 887, 888, 889, 893, 894, 895, 898, 899, 900, 901, 902, 903, 933, 934, 935, 939, 940, 941, 942, 943, 944, 945, 946, 947, 962, 963, 964, 965, 967, 968, 970, 971, 972, 973, 974, 975, 978, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 1006, 1007, 1008, 1009, 1010, 1012, 1013, 1018, 1019, 1020, 1022, 1023, 1024, 1026, 1027, 1029, 1030, 1031, 1032, 1033, 1034, 1036, 1037, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1050, 1061, 1064, 1068, 1069, 1072, 1075, 1076, 1077, 1079, 1081, 1082, 1083, 1084, 1086, 1087, 1088, 1089, 1090, 1091, 1092, 1093, 1094, 1105, 1106, 1107, 1108, 1145, 1146, 1160, 1161, 1162, 1166, 1197, 1198, 1199, 1204, 1205, 1206, 1210, 1212, 1213, 1215, 1235, 1236, 1237, 1242, 1243, 1244, 1245, 1263, 1264, 1265, 1270, 1271, 1272, 1304, 1305, 1306, 1311, 1315, 1316, 1318, 1322, 1323, 1324, 1325, 1326, 1327, 1328, 1329, 1331, 1332, 1333, 1334, 1335, 1337, 1338, 1339, 1341, 1348, 1378, 1379, 1381, 1382, 1383, 1388, 1389, 1390, 1392, 1393, 1394, 1395, 1399, 1400, 1401, 1406, 1407, 1409, 1410, 1412, 1435, 1436, 1437, 1438, 1440, 1442, 1443, 1444, 1446, 1447, 1448, 1454, 1455, 1456, 1457, 1459, 1460, 1461, 1462, 1467, 1468, 1469, 1471, 1473, 1474, 1475, 1476, 1477, 1479, 1481, 1482, 1484, 1497, 1498, 1499, 1513, 1515, 1516, 1517, 1518, 1519, 1520, 1521, 1522, 1523, 1542, 1543, 1544, 1545, 1547, 1548, 1549, 1569, 1570, 1571, 1572, 1573, 1574, 1575, 1576, 1577, 1591, 1592, 1593, 1594, 1595, 1596, 1600, 1602, 1603, 1617, 1618, 1619, 1620, 1621, 1635, 1636, 1641, 1642, 1643, 1644, 1645, 1646, 1647, 1650, 1651, 1655, 1656, 1657, 1658, 1659, 1660, 1677, 1678, 1680, 1681, 1682, 1691, 1692, 1693, 1694, 1725, 1739, 1740, 1741, 1742, 1743, 1760, 1761, 1762], "summary": {"covered_lines": 580, "num_statements": 633, "percent_covered": 91.62717219589257, "percent_covered_display": "92", "missing_lines": 53, "excluded_lines": 0, "percent_statements_covered": 91.62717219589257, "percent_statements_covered_display": "92"}, "missing_lines": [107, 125, 126, 127, 145, 146, 178, 179, 276, 277, 279, 395, 396, 397, 398, 399, 422, 438, 468, 469, 602, 603, 675, 676, 683, 713, 714, 723, 724, 727, 728, 735, 738, 739, 746, 751, 784, 785, 855, 862, 866, 884, 896, 897, 969, 1025, 1028, 1035, 1038, 1078, 1085, 1211, 1514], "excluded_lines": [], "start_line": 49}, "": {"executed_lines": [6, 8, 10, 11, 12, 13, 14, 15, 17, 19, 21, 22, 23, 24, 25, 26, 39, 49, 52, 83, 93, 106, 109, 199, 218, 250, 299, 331, 388, 403, 443, 474, 488, 516, 529, 550, 642, 759, 810, 833, 869, 905, 949, 983, 998, 1052, 1096, 1118, 1148, 1170, 1221, 1247, 1274, 1358, 1414, 1488, 1506, 1529, 1557, 1583, 1605, 1627, 1666, 1689, 1731, 1745], "summary": {"covered_lines": 66, "num_statements": 66, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\data\\_async_relationships.py": {"executed_lines": [10, 12, 14, 15, 18, 28, 51, 54, 55, 57, 58, 59, 61, 64, 66, 74, 94, 96, 98, 99, 100, 102, 105, 107, 114, 123, 124, 125, 126, 128, 140, 141, 142, 143, 144, 145, 147, 171, 172, 173, 174, 175, 176, 177, 178, 180, 210, 211, 213, 214, 215, 220, 228, 229, 230, 231, 232, 233, 234, 236, 237, 238, 240, 241, 242, 244, 250, 260, 261, 262, 263], "summary": {"covered_lines": 71, "num_statements": 71, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"_AsyncRelationshipOperationsMixin._create_one_to_many_relationship": {"executed_lines": [51, 54, 55, 57, 58, 59, 61, 64, 66], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 28}, "_AsyncRelationshipOperationsMixin._create_many_to_many_relationship": {"executed_lines": [94, 96, 98, 99, 100, 102, 105, 107], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 74}, "_AsyncRelationshipOperationsMixin._delete_relationship": {"executed_lines": [123, 124, 125, 126], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 114}, "_AsyncRelationshipOperationsMixin._get_relationship": {"executed_lines": [140, 141, 142, 143, 144, 145], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 128}, "_AsyncRelationshipOperationsMixin._list_relationships": {"executed_lines": [171, 172, 173, 174, 175, 176, 177, 178], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 147}, "_AsyncRelationshipOperationsMixin._list_table_relationships": {"executed_lines": [210, 211, 213, 214, 215, 220, 228, 229, 230, 231, 232, 233, 234, 236, 237, 238, 240, 241, 242, 244], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 180}, "_AsyncRelationshipOperationsMixin._extract_id_from_header": {"executed_lines": [260, 261, 262, 263], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 250}, "": {"executed_lines": [10, 12, 14, 15, 18, 28, 74, 114, 128, 147, 180, 250], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_AsyncRelationshipOperationsMixin": {"executed_lines": [51, 54, 55, 57, 58, 59, 61, 64, 66, 94, 96, 98, 99, 100, 102, 105, 107, 123, 124, 125, 126, 140, 141, 142, 143, 144, 145, 171, 172, 173, 174, 175, 176, 177, 178, 210, 211, 213, 214, 215, 220, 228, 229, 230, 231, 232, 233, 234, 236, 237, 238, 240, 241, 242, 244, 260, 261, 262, 263], "summary": {"covered_lines": 59, "num_statements": 59, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 18}, "": {"executed_lines": [10, 12, 14, 15, 18, 28, 74, 114, 128, 147, 180, 250], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\data\\_async_upload.py": {"executed_lines": [6, 8, 11, 14, 43, 46, 49, 50, 51, 52, 53, 54, 56, 59, 61, 63, 64, 65, 70, 72, 73, 76, 77, 78, 80, 90, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 109, 110, 112, 114, 115, 117, 147, 148, 150, 151, 152, 153, 154, 155, 156, 157, 158, 161, 162, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 177, 178, 179, 180, 181, 182, 184, 185, 186, 193, 194, 195], "summary": {"covered_lines": 83, "num_statements": 87, "percent_covered": 95.40229885057471, "percent_covered_display": "95", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 95.40229885057471, "percent_statements_covered_display": "95"}, "missing_lines": [66, 67, 176, 183], "excluded_lines": [], "functions": {"_AsyncFileUploadMixin._upload_file": {"executed_lines": [43, 46, 49, 50, 51, 52, 53, 54, 56, 59, 61, 63, 64, 65, 70, 72, 73, 76, 77, 78], "summary": {"covered_lines": 20, "num_statements": 22, "percent_covered": 90.9090909090909, "percent_covered_display": "91", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 90.9090909090909, "percent_statements_covered_display": "91"}, "missing_lines": [66, 67], "excluded_lines": [], "start_line": 14}, "_AsyncFileUploadMixin._upload_file_small": {"executed_lines": [90, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 109, 110, 112, 114, 115], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 80}, "_AsyncFileUploadMixin._upload_file_chunk": {"executed_lines": [147, 148, 150, 151, 152, 153, 154, 155, 156, 157, 158, 161, 162, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 177, 178, 179, 180, 181, 182, 184, 185, 186, 193, 194, 195], "summary": {"covered_lines": 37, "num_statements": 39, "percent_covered": 94.87179487179488, "percent_covered_display": "95", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 94.87179487179488, "percent_statements_covered_display": "95"}, "missing_lines": [176, 183], "excluded_lines": [], "start_line": 117}, "": {"executed_lines": [6, 8, 11, 14, 80, 117], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_AsyncFileUploadMixin": {"executed_lines": [43, 46, 49, 50, 51, 52, 53, 54, 56, 59, 61, 63, 64, 65, 70, 72, 73, 76, 77, 78, 90, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 109, 110, 112, 114, 115, 147, 148, 150, 151, 152, 153, 154, 155, 156, 157, 158, 161, 162, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 177, 178, 179, 180, 181, 182, 184, 185, 186, 193, 194, 195], "summary": {"covered_lines": 77, "num_statements": 81, "percent_covered": 95.06172839506173, "percent_covered_display": "95", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 95.06172839506173, "percent_statements_covered_display": "95"}, "missing_lines": [66, 67, 176, 183], "excluded_lines": [], "start_line": 11}, "": {"executed_lines": [6, 8, 11, 14, 80, 117], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\operations\\__init__.py": {"executed_lines": [11, 13], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [11, 13], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [11, 13], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\operations\\async_batch.py": {"executed_lines": [6, 8, 10, 12, 13, 14, 34, 35, 36, 37, 42, 47, 64, 75, 76, 78, 101, 103, 116, 118, 128, 131, 149, 150, 151, 153, 154, 156, 157, 165, 177, 178, 180, 197, 199, 219, 221, 243, 245, 265, 267, 286, 287, 288, 289, 290, 291, 292, 293, 295, 296, 299, 315, 316, 318, 341, 351, 360, 362, 369, 371, 385, 387, 398, 400, 412, 414, 431, 433, 447, 449, 456, 458, 465, 467, 503, 523, 534, 535, 537, 554, 555, 556, 564, 574, 575, 577, 591, 592, 593, 594, 596, 598, 599, 600, 601, 605, 607, 629, 630, 631, 632, 633, 634, 636, 637, 638, 639, 643, 645, 646, 647, 651, 653, 654, 655, 656, 657, 658, 660, 662, 681, 682, 683, 684, 685, 686, 687, 688, 689, 690, 698, 727, 728, 729, 730, 731, 732, 733, 734, 736, 751, 752, 753, 755, 773, 774, 777, 793, 794, 796, 802], "summary": {"covered_lines": 152, "num_statements": 152, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [44, 45], "functions": {"AsyncChangeSetRecordOperations.__init__": {"executed_lines": [76], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 75}, "AsyncChangeSetRecordOperations.create": {"executed_lines": [101], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 78}, "AsyncChangeSetRecordOperations.update": {"executed_lines": [116], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 103}, "AsyncChangeSetRecordOperations.delete": {"executed_lines": [128], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 118}, "AsyncChangeSet.__init__": {"executed_lines": [150, 151], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 149}, "AsyncChangeSet.__aenter__": {"executed_lines": [154], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 153}, "AsyncChangeSet.__aexit__": {"executed_lines": [157], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 156}, "AsyncBatchRecordOperations.__init__": {"executed_lines": [178], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 177}, "AsyncBatchRecordOperations.create": {"executed_lines": [197], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 180}, "AsyncBatchRecordOperations.update": {"executed_lines": [219], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 199}, "AsyncBatchRecordOperations.delete": {"executed_lines": [243], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 221}, "AsyncBatchRecordOperations.get": {"executed_lines": [265], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 245}, "AsyncBatchRecordOperations.upsert": {"executed_lines": [286, 287, 288, 289, 290, 291, 292, 293, 295, 296], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 267}, "AsyncBatchTableOperations.__init__": {"executed_lines": [316], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 315}, "AsyncBatchTableOperations.create": {"executed_lines": [341], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 318}, "AsyncBatchTableOperations.delete": {"executed_lines": [360], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 351}, "AsyncBatchTableOperations.get": {"executed_lines": [369], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 362}, "AsyncBatchTableOperations.list": {"executed_lines": [385], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 371}, "AsyncBatchTableOperations.add_columns": {"executed_lines": [398], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 387}, "AsyncBatchTableOperations.remove_columns": {"executed_lines": [412], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 400}, "AsyncBatchTableOperations.create_one_to_many_relationship": {"executed_lines": [431], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 414}, "AsyncBatchTableOperations.create_many_to_many_relationship": {"executed_lines": [447], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 433}, "AsyncBatchTableOperations.delete_relationship": {"executed_lines": [456], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 449}, "AsyncBatchTableOperations.get_relationship": {"executed_lines": [465], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 458}, "AsyncBatchTableOperations.create_lookup_field": {"executed_lines": [503], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 467}, "AsyncBatchQueryOperations.__init__": {"executed_lines": [535], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 534}, "AsyncBatchQueryOperations.sql": {"executed_lines": [554, 555, 556], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 537}, "AsyncBatchDataFrameOperations.__init__": {"executed_lines": [575], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 574}, "AsyncBatchDataFrameOperations.create": {"executed_lines": [591, 592, 593, 594, 596, 598, 599, 600, 601, 605], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 577}, "AsyncBatchDataFrameOperations.update": {"executed_lines": [629, 630, 631, 632, 633, 634, 636, 637, 638, 639, 643, 645, 646, 647, 651, 653, 654, 655, 656, 657, 658, 660], "summary": {"covered_lines": 22, "num_statements": 22, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 607}, "AsyncBatchDataFrameOperations.delete": {"executed_lines": [681, 682, 683, 684, 685, 686, 687, 688, 689, 690], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 662}, "AsyncBatchRequest.__init__": {"executed_lines": [728, 729, 730, 731, 732, 733, 734], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 727}, "AsyncBatchRequest.changeset": {"executed_lines": [751, 752, 753], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 736}, "AsyncBatchRequest.execute": {"executed_lines": [773, 774], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 755}, "AsyncBatchOperations.__init__": {"executed_lines": [794], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 793}, "AsyncBatchOperations.new": {"executed_lines": [802], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 796}, "": {"executed_lines": [6, 8, 10, 12, 13, 14, 34, 35, 36, 37, 42, 47, 64, 75, 78, 103, 118, 131, 149, 153, 156, 165, 177, 180, 199, 221, 245, 267, 299, 315, 318, 351, 362, 371, 387, 400, 414, 433, 449, 458, 467, 523, 534, 537, 564, 574, 577, 607, 662, 698, 727, 736, 755, 777, 793, 796], "summary": {"covered_lines": 56, "num_statements": 56, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [44, 45], "start_line": 1}}, "classes": {"AsyncChangeSetRecordOperations": {"executed_lines": [76, 101, 116, 128], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 64}, "AsyncChangeSet": {"executed_lines": [150, 151, 154, 157], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 131}, "AsyncBatchRecordOperations": {"executed_lines": [178, 197, 219, 243, 265, 286, 287, 288, 289, 290, 291, 292, 293, 295, 296], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 165}, "AsyncBatchTableOperations": {"executed_lines": [316, 341, 360, 369, 385, 398, 412, 431, 447, 456, 465, 503], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 299}, "AsyncBatchQueryOperations": {"executed_lines": [535, 554, 555, 556], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 523}, "AsyncBatchDataFrameOperations": {"executed_lines": [575, 591, 592, 593, 594, 596, 598, 599, 600, 601, 605, 629, 630, 631, 632, 633, 634, 636, 637, 638, 639, 643, 645, 646, 647, 651, 653, 654, 655, 656, 657, 658, 660, 681, 682, 683, 684, 685, 686, 687, 688, 689, 690], "summary": {"covered_lines": 43, "num_statements": 43, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 564}, "AsyncBatchRequest": {"executed_lines": [728, 729, 730, 731, 732, 733, 734, 751, 752, 753, 773, 774], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 698}, "AsyncBatchOperations": {"executed_lines": [794, 802], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 777}, "": {"executed_lines": [6, 8, 10, 12, 13, 14, 34, 35, 36, 37, 42, 47, 64, 75, 78, 103, 118, 131, 149, 153, 156, 165, 177, 180, 199, 221, 245, 267, 299, 315, 318, 351, 362, 371, 387, 400, 414, 433, 449, 458, 467, 523, 534, 537, 564, 574, 577, 607, 662, 698, 727, 736, 755, 777, 793, 796], "summary": {"covered_lines": 56, "num_statements": 56, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [44, 45], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\operations\\async_dataframe.py": {"executed_lines": [6, 8, 10, 12, 18, 21, 51, 52, 56, 82, 83, 84, 85, 89, 151, 152, 153, 154, 155, 156, 160, 165, 167, 168, 179, 181, 182, 183, 187, 217, 218, 220, 221, 223, 226, 227, 228, 233, 235, 236, 238, 242, 280, 281, 282, 283, 284, 285, 287, 288, 289, 290, 294, 296, 297, 298, 301, 304, 305, 306, 307, 308, 310, 311, 313, 317, 350, 351, 353, 354, 355, 357, 358, 359, 363, 365, 366, 367, 368], "summary": {"covered_lines": 79, "num_statements": 79, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [14, 15], "functions": {"AsyncDataFrameOperations.__init__": {"executed_lines": [52], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 51}, "AsyncDataFrameOperations.sql": {"executed_lines": [82, 83, 84, 85], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 56}, "AsyncDataFrameOperations.get": {"executed_lines": [151, 152, 153, 154, 155, 156, 160, 165, 167, 168, 179, 181, 182, 183], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 89}, "AsyncDataFrameOperations.create": {"executed_lines": [217, 218, 220, 221, 223, 226, 227, 228, 233, 235, 236, 238], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 187}, "AsyncDataFrameOperations.update": {"executed_lines": [280, 281, 282, 283, 284, 285, 287, 288, 289, 290, 294, 296, 297, 298, 301, 304, 305, 306, 307, 308, 310, 311, 313], "summary": {"covered_lines": 23, "num_statements": 23, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 242}, "AsyncDataFrameOperations.delete": {"executed_lines": [350, 351, 353, 354, 355, 357, 358, 359, 363, 365, 366, 367, 368], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 317}, "": {"executed_lines": [6, 8, 10, 12, 18, 21, 51, 56, 89, 187, 242, 317], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [14, 15], "start_line": 1}}, "classes": {"AsyncDataFrameOperations": {"executed_lines": [52, 82, 83, 84, 85, 151, 152, 153, 154, 155, 156, 160, 165, 167, 168, 179, 181, 182, 183, 217, 218, 220, 221, 223, 226, 227, 228, 233, 235, 236, 238, 280, 281, 282, 283, 284, 285, 287, 288, 289, 290, 294, 296, 297, 298, 301, 304, 305, 306, 307, 308, 310, 311, 313, 350, 351, 353, 354, 355, 357, 358, 359, 363, 365, 366, 367, 368], "summary": {"covered_lines": 67, "num_statements": 67, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 21}, "": {"executed_lines": [6, 8, 10, 12, 18, 21, 51, 56, 89, 187, 242, 317], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [14, 15], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\operations\\async_files.py": {"executed_lines": [6, 8, 14, 17, 35, 36, 40, 104, 105], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [10, 11], "functions": {"AsyncFileOperations.__init__": {"executed_lines": [36], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 35}, "AsyncFileOperations.upload": {"executed_lines": [104, 105], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 40}, "": {"executed_lines": [6, 8, 14, 17, 35, 40], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [10, 11], "start_line": 1}}, "classes": {"AsyncFileOperations": {"executed_lines": [36, 104, 105], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 17}, "": {"executed_lines": [6, 8, 14, 17, 35, 40], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [10, 11], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\operations\\async_query.py": {"executed_lines": [6, 8, 10, 11, 17, 20, 39, 40, 44, 93, 94, 95, 99, 129, 138, 151, 152, 153, 154, 155, 156, 157, 160, 161, 163, 164, 165, 166, 167, 168, 169, 178, 179, 183, 208, 209, 213, 246, 247, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 267, 268, 269, 270, 271, 272, 273, 274, 276, 286, 287, 291, 327, 328, 329, 330, 331, 336, 337, 338, 339, 347, 372, 373, 377, 407, 408, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 423, 424, 425, 426, 427, 428, 430, 440, 441, 445, 477, 478, 479, 480, 481, 486, 490, 526, 527, 528, 529, 530, 535, 536, 537, 538], "summary": {"covered_lines": 115, "num_statements": 115, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [13, 14], "functions": {"AsyncQueryOperations.__init__": {"executed_lines": [40], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 39}, "AsyncQueryOperations.sql": {"executed_lines": [93, 94, 95], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 44}, "AsyncQueryOperations.sql_columns": {"executed_lines": [129, 138, 151, 152, 153, 154, 155, 156, 157, 160, 161, 163, 164, 165, 166, 167, 168, 169, 178, 179], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 99}, "AsyncQueryOperations.sql_select": {"executed_lines": [208, 209], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 183}, "AsyncQueryOperations.sql_joins": {"executed_lines": [246, 247, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 267, 268, 269, 270, 271, 272, 273, 274, 276, 286, 287], "summary": {"covered_lines": 25, "num_statements": 25, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 213}, "AsyncQueryOperations.sql_join": {"executed_lines": [327, 328, 329, 330, 331, 336, 337, 338, 339], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 291}, "AsyncQueryOperations.odata_select": {"executed_lines": [372, 373], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 347}, "AsyncQueryOperations.odata_expands": {"executed_lines": [407, 408, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 423, 424, 425, 426, 427, 428, 430, 440, 441], "summary": {"covered_lines": 22, "num_statements": 22, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 377}, "AsyncQueryOperations.odata_expand": {"executed_lines": [477, 478, 479, 480, 481, 486], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 445}, "AsyncQueryOperations.odata_bind": {"executed_lines": [526, 527, 528, 529, 530, 535, 536, 537, 538], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 490}, "": {"executed_lines": [6, 8, 10, 11, 17, 20, 39, 44, 99, 183, 213, 291, 347, 377, 445, 490], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [13, 14], "start_line": 1}}, "classes": {"AsyncQueryOperations": {"executed_lines": [40, 93, 94, 95, 129, 138, 151, 152, 153, 154, 155, 156, 157, 160, 161, 163, 164, 165, 166, 167, 168, 169, 178, 179, 208, 209, 246, 247, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 267, 268, 269, 270, 271, 272, 273, 274, 276, 286, 287, 327, 328, 329, 330, 331, 336, 337, 338, 339, 372, 373, 407, 408, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 423, 424, 425, 426, 427, 428, 430, 440, 441, 477, 478, 479, 480, 481, 486, 526, 527, 528, 529, 530, 535, 536, 537, 538], "summary": {"covered_lines": 99, "num_statements": 99, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 20}, "": {"executed_lines": [6, 8, 10, 11, 17, 20, 39, 44, 99, 183, 213, 291, 347, 377, 445, 490], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [13, 14], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\operations\\async_records.py": {"executed_lines": [6, 8, 10, 11, 17, 20, 46, 47, 57, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 110, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 174, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 228, 229, 262, 263, 331, 428, 429, 430, 431, 440, 445, 446, 447, 449, 450, 451, 462, 464, 468, 516, 517, 518, 519, 520, 521, 522, 523, 525, 526, 527, 528, 529, 530, 532, 533, 534, 535], "summary": {"covered_lines": 86, "num_statements": 86, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 18, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [13, 14, 51, 52, 53, 54, 55, 56, 168, 169, 170, 171, 172, 173, 260, 261, 329, 330], "functions": {"AsyncRecordOperations.__init__": {"executed_lines": [47], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 46}, "AsyncRecordOperations.create": {"executed_lines": [94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 57}, "AsyncRecordOperations.update": {"executed_lines": [155, 156, 157, 158, 159, 160, 161, 162, 163, 164], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 110}, "AsyncRecordOperations.delete": {"executed_lines": [210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 174}, "AsyncRecordOperations.get": {"executed_lines": [428, 429, 430, 431, 440, 445, 446, 447, 449, 464], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 331}, "AsyncRecordOperations.get._paged": {"executed_lines": [450, 451, 462], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 449}, "AsyncRecordOperations.upsert": {"executed_lines": [516, 517, 518, 519, 520, 521, 522, 523, 525, 526, 527, 528, 529, 530, 532, 533, 534, 535], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 468}, "": {"executed_lines": [6, 8, 10, 11, 17, 20, 46, 57, 110, 174, 228, 229, 262, 263, 331, 468], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 12, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [13, 14, 51, 53, 54, 56, 168, 170, 171, 173, 261, 330], "start_line": 1}}, "classes": {"AsyncRecordOperations": {"executed_lines": [47, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 428, 429, 430, 431, 440, 445, 446, 447, 449, 450, 451, 462, 464, 516, 517, 518, 519, 520, 521, 522, 523, 525, 526, 527, 528, 529, 530, 532, 533, 534, 535], "summary": {"covered_lines": 70, "num_statements": 70, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 6, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [52, 55, 169, 172, 260, 329], "start_line": 20}, "": {"executed_lines": [6, 8, 10, 11, 17, 20, 46, 57, 110, 174, 228, 229, 262, 263, 331, 468], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 12, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [13, 14, 51, 53, 54, 56, 168, 170, 171, 173, 261, 330], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\operations\\async_tables.py": {"executed_lines": [6, 8, 10, 16, 17, 18, 19, 25, 28, 64, 65, 69, 134, 135, 142, 146, 163, 164, 168, 188, 189, 190, 191, 192, 196, 244, 245, 249, 277, 278, 282, 310, 311, 315, 373, 374, 379, 389, 429, 430, 434, 443, 462, 463, 467, 486, 487, 488, 489, 490, 494, 559, 560, 571, 575, 626, 627, 628, 629, 638, 660, 661, 662, 666, 690, 691, 695, 735, 736, 740, 769, 770, 774, 814, 815], "summary": {"covered_lines": 75, "num_statements": 75, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [21, 22], "functions": {"AsyncTableOperations.__init__": {"executed_lines": [65], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 64}, "AsyncTableOperations.create": {"executed_lines": [134, 135, 142], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 69}, "AsyncTableOperations.delete": {"executed_lines": [163, 164], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 146}, "AsyncTableOperations.get": {"executed_lines": [188, 189, 190, 191, 192], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 168}, "AsyncTableOperations.list": {"executed_lines": [244, 245], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 196}, "AsyncTableOperations.add_columns": {"executed_lines": [277, 278], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 249}, "AsyncTableOperations.remove_columns": {"executed_lines": [310, 311], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 282}, "AsyncTableOperations.create_one_to_many_relationship": {"executed_lines": [373, 374, 379], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 315}, "AsyncTableOperations.create_many_to_many_relationship": {"executed_lines": [429, 430, 434], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 389}, "AsyncTableOperations.delete_relationship": {"executed_lines": [462, 463], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 443}, "AsyncTableOperations.get_relationship": {"executed_lines": [486, 487, 488, 489, 490], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 467}, "AsyncTableOperations.create_lookup_field": {"executed_lines": [559, 560, 571], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 494}, "AsyncTableOperations.create_alternate_key": {"executed_lines": [626, 627, 628, 629], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 575}, "AsyncTableOperations.get_alternate_keys": {"executed_lines": [660, 661, 662], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 638}, "AsyncTableOperations.delete_alternate_key": {"executed_lines": [690, 691], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 666}, "AsyncTableOperations.list_columns": {"executed_lines": [735, 736], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 695}, "AsyncTableOperations.list_relationships": {"executed_lines": [769, 770], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 740}, "AsyncTableOperations.list_table_relationships": {"executed_lines": [814, 815], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 774}, "": {"executed_lines": [6, 8, 10, 16, 17, 18, 19, 25, 28, 64, 69, 146, 168, 196, 249, 282, 315, 389, 443, 467, 494, 575, 638, 666, 695, 740, 774], "summary": {"covered_lines": 27, "num_statements": 27, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [21, 22], "start_line": 1}}, "classes": {"AsyncTableOperations": {"executed_lines": [65, 134, 135, 142, 163, 164, 188, 189, 190, 191, 192, 244, 245, 277, 278, 310, 311, 373, 374, 379, 429, 430, 434, 462, 463, 486, 487, 488, 489, 490, 559, 560, 571, 626, 627, 628, 629, 660, 661, 662, 690, 691, 735, 736, 769, 770, 814, 815], "summary": {"covered_lines": 48, "num_statements": 48, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 28}, "": {"executed_lines": [6, 8, 10, 16, 17, 18, 19, 25, 28, 64, 69, 146, 168, 196, 249, 282, 315, 389, 443, 467, 494, 575, 638, 666, 695, 740, 774], "summary": {"covered_lines": 27, "num_statements": 27, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [21, 22], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\claude_skill\\__init__.py": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\client.py": {"executed_lines": [4, 6, 7, 8, 10, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 25, 93, 99, 100, 101, 102, 103, 104, 105, 106, 109, 110, 111, 112, 113, 114, 116, 126, 127, 133, 135, 136, 138, 139, 140, 141, 145, 157, 158, 159, 160, 162, 168, 170, 187, 188, 189, 190, 191, 192, 193, 194, 195, 197, 199, 200, 203, 238, 244, 245, 246, 248, 296, 301, 303, 338, 343, 345, 428, 433, 434, 436, 447, 483, 488, 491, 514, 519, 521, 592, 597, 604, 625, 630, 632, 649, 654, 656, 688, 693, 695, 721, 726, 729, 761, 766, 777, 798, 799, 802], "summary": {"covered_lines": 105, "num_statements": 105, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"DataverseClient.__init__": {"executed_lines": [99, 100, 101, 102, 103, 104, 105, 106, 109, 110, 111, 112, 113, 114], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 93}, "DataverseClient._get_odata": {"executed_lines": [126, 127, 133], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 116}, "DataverseClient._scoped_odata": {"executed_lines": [138, 139, 140, 141], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 136}, "DataverseClient.__enter__": {"executed_lines": [157, 158, 159, 160], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 145}, "DataverseClient.__exit__": {"executed_lines": [168], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 162}, "DataverseClient.close": {"executed_lines": [187, 188, 189, 190, 191, 192, 193, 194, 195], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 170}, "DataverseClient._check_closed": {"executed_lines": [199, 200], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 197}, "DataverseClient.create": {"executed_lines": [238, 244, 245, 246], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 203}, "DataverseClient.update": {"executed_lines": [296, 301], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 248}, "DataverseClient.delete": {"executed_lines": [338, 343], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 303}, "DataverseClient.get": {"executed_lines": [428, 433, 434, 436], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 345}, "DataverseClient.query_sql": {"executed_lines": [483, 488], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 447}, "DataverseClient.get_table_info": {"executed_lines": [514, 519], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 491}, "DataverseClient.create_table": {"executed_lines": [592, 597], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 521}, "DataverseClient.delete_table": {"executed_lines": [625, 630], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 604}, "DataverseClient.list_tables": {"executed_lines": [649, 654], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 632}, "DataverseClient.create_columns": {"executed_lines": [688, 693], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 656}, "DataverseClient.delete_columns": {"executed_lines": [721, 726], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 695}, "DataverseClient.upload_file": {"executed_lines": [761, 766], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 729}, "DataverseClient.flush_cache": {"executed_lines": [798, 799], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 777}, "": {"executed_lines": [4, 6, 7, 8, 10, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 25, 93, 116, 135, 136, 145, 162, 170, 197, 203, 248, 303, 345, 447, 491, 521, 604, 632, 656, 695, 729, 777, 802], "summary": {"covered_lines": 38, "num_statements": 38, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"DataverseClient": {"executed_lines": [99, 100, 101, 102, 103, 104, 105, 106, 109, 110, 111, 112, 113, 114, 126, 127, 133, 138, 139, 140, 141, 157, 158, 159, 160, 168, 187, 188, 189, 190, 191, 192, 193, 194, 195, 199, 200, 238, 244, 245, 246, 296, 301, 338, 343, 428, 433, 434, 436, 483, 488, 514, 519, 592, 597, 625, 630, 649, 654, 688, 693, 721, 726, 761, 766, 798, 799], "summary": {"covered_lines": 67, "num_statements": 67, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 25}, "": {"executed_lines": [4, 6, 7, 8, 10, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 25, 93, 116, 135, 136, 145, 162, 170, 197, 203, 248, 303, 345, 447, 491, 521, 604, 632, 656, 695, 729, 777, 802], "summary": {"covered_lines": 38, "num_statements": 38, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\common\\__init__.py": {"executed_lines": [10], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [10], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [10], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\common\\constants.py": {"executed_lines": [12, 13, 14, 15, 16, 21, 22, 24, 25, 27, 28, 30, 31], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [12, 13, 14, 15, 16, 21, 22, 24, 25, 27, 28, 30, 31], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [12, 13, 14, 15, 16, 21, 22, 24, 25, 27, 28, 30, 31], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\core\\__init__.py": {"executed_lines": [11], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [11], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [11], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\core\\_auth.py": {"executed_lines": [12, 14, 16, 19, 20, 30, 31, 34, 43, 44, 45, 46, 48, 58, 59], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"_AuthManager.__init__": {"executed_lines": [44, 45, 46], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 43}, "_AuthManager._acquire_token": {"executed_lines": [58, 59], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 48}, "": {"executed_lines": [12, 14, 16, 19, 20, 30, 31, 34, 43, 48], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_TokenPair": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 20}, "_AuthManager": {"executed_lines": [44, 45, 46, 58, 59], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 34}, "": {"executed_lines": [12, 14, 16, 19, 20, 30, 31, 34, 43, 48], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\core\\_error_codes.py": {"executed_lines": [12, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 28, 44, 45, 46, 47, 48, 49, 50, 51, 54, 57, 58, 59, 60, 61, 62, 63, 66, 81, 84, 93, 96, 108], "summary": {"covered_lines": 36, "num_statements": 36, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"_http_subcode": {"executed_lines": [93], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 84}, "_is_transient_status": {"executed_lines": [108], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 96}, "": {"executed_lines": [12, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 28, 44, 45, 46, 47, 48, 49, 50, 51, 54, 57, 58, 59, 60, 61, 62, 63, 66, 81, 84, 96], "summary": {"covered_lines": 34, "num_statements": 34, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [12, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 28, 44, 45, 46, 47, 48, 49, 50, 51, 54, 57, 58, 59, 60, 61, 62, 63, 66, 81, 84, 93, 96, 108], "summary": {"covered_lines": 36, "num_statements": 36, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\core\\_http.py": {"executed_lines": [12, 14, 15, 17, 23, 45, 53, 54, 55, 56, 57, 59, 77, 78, 79, 81, 82, 86, 87, 88, 89, 92, 93, 101, 102, 103, 104, 105, 106, 108, 111, 112, 120, 121, 122, 123, 130, 131, 132, 133, 134, 136, 141, 142, 143], "summary": {"covered_lines": 45, "num_statements": 46, "percent_covered": 97.82608695652173, "percent_covered_display": "98", "missing_lines": 1, "excluded_lines": 2, "percent_statements_covered": 97.82608695652173, "percent_statements_covered_display": "98"}, "missing_lines": [90], "excluded_lines": [19, 20], "functions": {"_HttpClient.__init__": {"executed_lines": [53, 54, 55, 56, 57], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 45}, "_HttpClient._request": {"executed_lines": [77, 78, 79, 81, 82, 86, 87, 88, 89, 92, 93, 101, 102, 103, 104, 105, 106, 108, 111, 112, 120, 121, 122, 123, 130, 131, 132, 133, 134], "summary": {"covered_lines": 29, "num_statements": 30, "percent_covered": 96.66666666666667, "percent_covered_display": "97", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 96.66666666666667, "percent_statements_covered_display": "97"}, "missing_lines": [90], "excluded_lines": [], "start_line": 59}, "_HttpClient.close": {"executed_lines": [141, 142, 143], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 136}, "": {"executed_lines": [12, 14, 15, 17, 23, 45, 59, 136], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [19, 20], "start_line": 1}}, "classes": {"_HttpClient": {"executed_lines": [53, 54, 55, 56, 57, 77, 78, 79, 81, 82, 86, 87, 88, 89, 92, 93, 101, 102, 103, 104, 105, 106, 108, 111, 112, 120, 121, 122, 123, 130, 131, 132, 133, 134, 141, 142, 143], "summary": {"covered_lines": 37, "num_statements": 38, "percent_covered": 97.36842105263158, "percent_covered_display": "97", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 97.36842105263158, "percent_statements_covered_display": "97"}, "missing_lines": [90], "excluded_lines": [], "start_line": 23}, "": {"executed_lines": [12, 14, 15, 17, 23, 45, 59, 136], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [19, 20], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\core\\_http_logger.py": {"executed_lines": [8, 10, 11, 12, 13, 14, 15, 16, 18, 21, 24, 25, 26, 29, 33, 34, 35, 38, 39, 40, 41, 43, 49, 53, 54, 57, 62, 63, 75, 83, 84, 85, 86, 87, 88, 89, 91, 101, 102, 103, 104, 105, 106, 107, 108, 111, 112, 113, 114, 117, 118, 119, 121, 130, 131, 133, 134, 136, 138, 140, 141, 142, 144, 145, 147, 148, 149, 150, 151, 152, 153, 154, 158, 160, 161, 162, 163, 164, 167, 168, 169], "summary": {"covered_lines": 81, "num_statements": 85, "percent_covered": 95.29411764705883, "percent_covered_display": "95", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 95.29411764705883, "percent_statements_covered_display": "95"}, "missing_lines": [115, 116, 155, 156], "excluded_lines": [], "functions": {"_HttpLogger.__init__": {"executed_lines": [25, 26, 29, 33, 34, 35, 38, 39, 40, 41, 43, 49, 53, 54, 57, 62, 63], "summary": {"covered_lines": 17, "num_statements": 17, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 24}, "_HttpLogger.log_request": {"executed_lines": [83, 84, 85, 86, 87, 88, 89], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 75}, "_HttpLogger.log_response": {"executed_lines": [101, 102, 103, 104, 105, 106, 107, 108, 111, 112, 113, 114, 117, 118, 119], "summary": {"covered_lines": 15, "num_statements": 17, "percent_covered": 88.23529411764706, "percent_covered_display": "88", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 88.23529411764706, "percent_statements_covered_display": "88"}, "missing_lines": [115, 116], "excluded_lines": [], "start_line": 91}, "_HttpLogger.log_error": {"executed_lines": [130, 131], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 121}, "_HttpLogger.body_logging_enabled": {"executed_lines": [136], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 134}, "_HttpLogger.close": {"executed_lines": [140, 141, 142], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 138}, "_HttpLogger._redact_headers": {"executed_lines": [145], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 144}, "_HttpLogger._truncate_body": {"executed_lines": [148, 149, 150, 151, 152, 153, 154, 158, 160, 161, 162, 163, 164, 167, 168, 169], "summary": {"covered_lines": 16, "num_statements": 18, "percent_covered": 88.88888888888889, "percent_covered_display": "89", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 88.88888888888889, "percent_statements_covered_display": "89"}, "missing_lines": [155, 156], "excluded_lines": [], "start_line": 147}, "": {"executed_lines": [8, 10, 11, 12, 13, 14, 15, 16, 18, 21, 24, 75, 91, 121, 133, 134, 138, 144, 147], "summary": {"covered_lines": 19, "num_statements": 19, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_HttpLogger": {"executed_lines": [25, 26, 29, 33, 34, 35, 38, 39, 40, 41, 43, 49, 53, 54, 57, 62, 63, 83, 84, 85, 86, 87, 88, 89, 101, 102, 103, 104, 105, 106, 107, 108, 111, 112, 113, 114, 117, 118, 119, 130, 131, 136, 140, 141, 142, 145, 148, 149, 150, 151, 152, 153, 154, 158, 160, 161, 162, 163, 164, 167, 168, 169], "summary": {"covered_lines": 62, "num_statements": 66, "percent_covered": 93.93939393939394, "percent_covered_display": "94", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 93.93939393939394, "percent_statements_covered_display": "94"}, "missing_lines": [115, 116, 155, 156], "excluded_lines": [], "start_line": 21}, "": {"executed_lines": [8, 10, 11, 12, 13, 14, 15, 16, 18, 21, 24, 75, 91, 121, 133, 134, 138, 144, 147], "summary": {"covered_lines": 19, "num_statements": 19, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\core\\config.py": {"executed_lines": [12, 14, 15, 21, 22, 40, 43, 44, 45, 47, 49, 50, 58], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [17, 18], "functions": {"DataverseConfig.from_env": {"executed_lines": [58], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 50}, "": {"executed_lines": [12, 14, 15, 21, 22, 40, 43, 44, 45, 47, 49, 50], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [17, 18], "start_line": 1}}, "classes": {"DataverseConfig": {"executed_lines": [58], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 22}, "": {"executed_lines": [12, 14, 15, 21, 22, 40, 43, 44, 45, 47, 49, 50], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [17, 18], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\core\\errors.py": {"executed_lines": [15, 16, 17, 20, 40, 50, 51, 52, 53, 54, 55, 56, 57, 58, 60, 67, 82, 94, 95, 98, 110, 111, 114, 126, 127, 130, 160, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 201], "summary": {"covered_lines": 44, "num_statements": 44, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [78, 79], "functions": {"DataverseError.__init__": {"executed_lines": [50, 51, 52, 53, 54, 55, 56, 57, 58], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 40}, "DataverseError.to_dict": {"executed_lines": [67], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 60}, "DataverseError.__repr__": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 1, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [79], "start_line": 78}, "ValidationError.__init__": {"executed_lines": [95], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 94}, "MetadataError.__init__": {"executed_lines": [111], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 110}, "SQLParseError.__init__": {"executed_lines": [127], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 126}, "HttpError.__init__": {"executed_lines": [175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 160}, "": {"executed_lines": [15, 16, 17, 20, 40, 60, 82, 94, 98, 110, 114, 126, 130, 160, 201], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 1, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [78], "start_line": 1}}, "classes": {"DataverseError": {"executed_lines": [50, 51, 52, 53, 54, 55, 56, 57, 58, 67], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 1, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [79], "start_line": 20}, "ValidationError": {"executed_lines": [95], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 82}, "MetadataError": {"executed_lines": [111], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 98}, "SQLParseError": {"executed_lines": [127], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 114}, "HttpError": {"executed_lines": [175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 130}, "": {"executed_lines": [15, 16, 17, 20, 40, 60, 82, 94, 98, 110, 114, 126, 130, 160, 201], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 1, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [78], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\core\\log_config.py": {"executed_lines": [11, 13, 14, 16, 18, 21, 31, 32, 35, 36, 61, 62, 63, 67, 68, 69, 72, 73, 74, 75], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"_default_redacted_headers": {"executed_lines": [32], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 31}, "LogConfig.__post_init__": {"executed_lines": [62, 63], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 61}, "": {"executed_lines": [11, 13, 14, 16, 18, 21, 31, 35, 36, 61, 67, 68, 69, 72, 73, 74, 75], "summary": {"covered_lines": 17, "num_statements": 17, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"LogConfig": {"executed_lines": [62, 63], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 36}, "": {"executed_lines": [11, 13, 14, 16, 18, 21, 31, 32, 35, 36, 61, 67, 68, 69, 72, 73, 74, 75], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\data\\__init__.py": {"executed_lines": [11], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [11], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [11], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\data\\_batch.py": {"executed_lines": [6, 8, 9, 11, 12, 13, 14, 15, 42, 50, 62, 73, 74, 76, 78, 79, 80, 87, 88, 90, 93, 94, 96, 97, 108, 114, 115, 116, 117, 118, 120, 121, 122, 124, 125, 127, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 168, 170, 171, 172, 176, 182, 183, 184, 185, 186, 188, 189, 190, 191, 192, 193, 194, 196, 197, 198, 199, 200, 201, 202, 203, 204, 206, 207, 209, 210, 211, 212, 213, 214, 215, 216, 224, 226, 227, 228, 232, 234, 235, 236, 238, 239, 240, 242, 243, 244, 245, 246, 247, 250, 251, 255, 256, 262, 263], "summary": {"covered_lines": 130, "num_statements": 130, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [39, 40], "functions": {"_BatchClient.execute": {"executed_lines": [73, 74, 76, 78, 79, 80, 87, 88, 90, 93, 94, 96, 97, 108], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 62}, "_BatchClient._resolve_all": {"executed_lines": [115, 116, 117, 118, 120, 121, 122, 124, 125], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 114}, "_BatchClient._resolve_item": {"executed_lines": [129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163], "summary": {"covered_lines": 35, "num_statements": 35, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 127}, "_BatchClient._resolve_one": {"executed_lines": [170, 171, 172, 176], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 168}, "_BatchClient._resolve_record_create": {"executed_lines": [183, 184, 185, 186], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 182}, "_BatchClient._resolve_record_update": {"executed_lines": [189, 190, 191, 192, 193, 194], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 188}, "_BatchClient._resolve_record_delete": {"executed_lines": [197, 198, 199, 200, 201, 202, 203, 204], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 196}, "_BatchClient._resolve_record_get": {"executed_lines": [207], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 206}, "_BatchClient._resolve_record_upsert": {"executed_lines": [210, 211, 212, 213, 214, 215, 216], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 209}, "_BatchClient._require_entity_metadata": {"executed_lines": [226, 227, 228, 232], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 224}, "_BatchClient._resolve_table_delete": {"executed_lines": [235, 236], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 234}, "_BatchClient._resolve_table_add_columns": {"executed_lines": [239, 240], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 238}, "_BatchClient._resolve_table_remove_columns": {"executed_lines": [243, 244, 245, 246, 247, 250, 251, 255, 256], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 242}, "_BatchClient._resolve_query_sql": {"executed_lines": [263], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 262}, "": {"executed_lines": [6, 8, 9, 11, 12, 13, 14, 15, 42, 50, 62, 114, 127, 168, 182, 188, 196, 206, 209, 224, 234, 238, 242, 262], "summary": {"covered_lines": 24, "num_statements": 24, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [39, 40], "start_line": 1}}, "classes": {"_BatchClient": {"executed_lines": [73, 74, 76, 78, 79, 80, 87, 88, 90, 93, 94, 96, 97, 108, 115, 116, 117, 118, 120, 121, 122, 124, 125, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 170, 171, 172, 176, 183, 184, 185, 186, 189, 190, 191, 192, 193, 194, 197, 198, 199, 200, 201, 202, 203, 204, 207, 210, 211, 212, 213, 214, 215, 216, 226, 227, 228, 232, 235, 236, 239, 240, 243, 244, 245, 246, 247, 250, 251, 255, 256, 263], "summary": {"covered_lines": 106, "num_statements": 106, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 50}, "": {"executed_lines": [6, 8, 9, 11, 12, 13, 14, 15, 42, 50, 62, 114, 127, 168, 182, 188, 196, 206, 209, 224, 234, 238, 242, 262], "summary": {"covered_lines": 24, "num_statements": 24, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [39, 40], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\data\\_batch_base.py": {"executed_lines": [9, 11, 12, 13, 14, 15, 17, 18, 19, 20, 25, 26, 27, 28, 33, 35, 36, 47, 48, 49, 50, 51, 54, 55, 56, 57, 58, 59, 62, 63, 64, 65, 66, 67, 70, 71, 72, 73, 74, 77, 78, 79, 80, 86, 87, 88, 89, 90, 91, 92, 95, 96, 97, 100, 101, 102, 105, 106, 107, 108, 111, 112, 113, 114, 117, 118, 119, 120, 123, 124, 125, 126, 127, 130, 131, 132, 133, 136, 137, 138, 141, 142, 143, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 162, 163, 164, 172, 173, 186, 187, 189, 191, 192, 193, 194, 196, 198, 199, 200, 202, 204, 205, 206, 216, 217, 220, 228, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 253, 256, 257, 258, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 283, 284, 285, 287, 288, 289, 290, 291, 292, 293, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 344, 345, 346, 347, 362, 372, 373, 379, 380, 382, 383, 385, 386, 388, 389, 390, 391, 393, 394, 396, 397, 399, 400, 402, 403, 413, 414, 415, 421, 426, 427, 428, 429, 431, 432, 434, 436, 441, 442, 444, 445, 446, 447, 448, 449, 450, 451, 452, 454, 455, 456, 458, 459, 460, 461, 462, 464, 467, 473, 474, 475, 476, 481, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 495, 496, 497, 498], "summary": {"covered_lines": 287, "num_statements": 288, "percent_covered": 99.65277777777777, "percent_covered_display": "99", "missing_lines": 1, "excluded_lines": 2, "percent_statements_covered": 99.65277777777777, "percent_statements_covered_display": "99"}, "missing_lines": [482], "excluded_lines": [30, 31], "functions": {"_ChangeSet.add_create": {"executed_lines": [191, 192, 193, 194], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 189}, "_ChangeSet.add_update": {"executed_lines": [198, 199, 200], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 196}, "_ChangeSet.add_delete": {"executed_lines": [204, 205, 206], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 202}, "_raise_top_level_batch_error": {"executed_lines": [236, 237, 238, 239, 240, 241, 242, 243, 244, 245], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 228}, "_extract_boundary": {"executed_lines": [257, 258], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 256}, "_split_multipart": {"executed_lines": [262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280], "summary": {"covered_lines": 19, "num_statements": 19, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 261}, "_parse_mime_part": {"executed_lines": [284, 285, 287, 288, 289, 290, 291, 292, 293], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 283}, "_parse_http_response_part": {"executed_lines": [297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 344, 345, 346, 347], "summary": {"covered_lines": 50, "num_statements": 50, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 296}, "_BatchBase.__init__": {"executed_lines": [373], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 372}, "_BatchBase._resolve_table_create": {"executed_lines": [380], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 379}, "_BatchBase._resolve_table_get": {"executed_lines": [383], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 382}, "_BatchBase._resolve_table_list": {"executed_lines": [386], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 385}, "_BatchBase._resolve_table_create_one_to_many": {"executed_lines": [389, 390, 391], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 388}, "_BatchBase._resolve_table_create_many_to_many": {"executed_lines": [394], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 393}, "_BatchBase._resolve_table_delete_relationship": {"executed_lines": [397], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 396}, "_BatchBase._resolve_table_get_relationship": {"executed_lines": [400], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 399}, "_BatchBase._resolve_table_create_lookup_field": {"executed_lines": [403, 413, 414, 415], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 402}, "_BatchBase._build_batch_body": {"executed_lines": [426, 427, 428, 429, 431, 432], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 421}, "_BatchBase._serialize_raw_request": {"executed_lines": [436, 441, 442, 444, 445, 446, 447, 448, 449, 450, 451, 452, 454, 455, 456], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 434}, "_BatchBase._serialize_changeset_item": {"executed_lines": [459, 460, 461, 462, 464, 467], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 458}, "_BatchBase._parse_batch_response": {"executed_lines": [474, 475, 476, 481, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 495, 496, 497, 498], "summary": {"covered_lines": 19, "num_statements": 20, "percent_covered": 95.0, "percent_covered_display": "95", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 95.0, "percent_statements_covered_display": "95"}, "missing_lines": [482], "excluded_lines": [], "start_line": 473}, "": {"executed_lines": [9, 11, 12, 13, 14, 15, 17, 18, 19, 20, 25, 26, 27, 28, 33, 35, 36, 47, 48, 49, 50, 51, 54, 55, 56, 57, 58, 59, 62, 63, 64, 65, 66, 67, 70, 71, 72, 73, 74, 77, 78, 79, 80, 86, 87, 88, 89, 90, 91, 92, 95, 96, 97, 100, 101, 102, 105, 106, 107, 108, 111, 112, 113, 114, 117, 118, 119, 120, 123, 124, 125, 126, 127, 130, 131, 132, 133, 136, 137, 138, 141, 142, 143, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 162, 163, 164, 172, 173, 186, 187, 189, 196, 202, 216, 217, 220, 228, 253, 256, 261, 283, 296, 362, 372, 379, 382, 385, 388, 393, 396, 399, 402, 421, 434, 458, 473], "summary": {"covered_lines": 127, "num_statements": 127, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [30, 31], "start_line": 1}}, "classes": {"_RecordCreate": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 48}, "_RecordUpdate": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 55}, "_RecordDelete": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 63}, "_RecordGet": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 71}, "_RecordUpsert": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 78}, "_TableCreate": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 87}, "_TableDelete": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 96}, "_TableGet": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 101}, "_TableList": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 106}, "_TableAddColumns": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 112}, "_TableRemoveColumns": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 118}, "_TableCreateOneToMany": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 124}, "_TableCreateManyToMany": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 131}, "_TableDeleteRelationship": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 137}, "_TableGetRelationship": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 142}, "_TableCreateLookupField": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 147}, "_QuerySql": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 163}, "_ChangeSet": {"executed_lines": [191, 192, 193, 194, 198, 199, 200, 204, 205, 206], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 173}, "_ChangeSetBatchItem": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 217}, "_BatchBase": {"executed_lines": [373, 380, 383, 386, 389, 390, 391, 394, 397, 400, 403, 413, 414, 415, 426, 427, 428, 429, 431, 432, 436, 441, 442, 444, 445, 446, 447, 448, 449, 450, 451, 452, 454, 455, 456, 459, 460, 461, 462, 464, 467, 474, 475, 476, 481, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 495, 496, 497, 498], "summary": {"covered_lines": 60, "num_statements": 61, "percent_covered": 98.36065573770492, "percent_covered_display": "98", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 98.36065573770492, "percent_statements_covered_display": "98"}, "missing_lines": [482], "excluded_lines": [], "start_line": 362}, "": {"executed_lines": [9, 11, 12, 13, 14, 15, 17, 18, 19, 20, 25, 26, 27, 28, 33, 35, 36, 47, 48, 49, 50, 51, 54, 55, 56, 57, 58, 59, 62, 63, 64, 65, 66, 67, 70, 71, 72, 73, 74, 77, 78, 79, 80, 86, 87, 88, 89, 90, 91, 92, 95, 96, 97, 100, 101, 102, 105, 106, 107, 108, 111, 112, 113, 114, 117, 118, 119, 120, 123, 124, 125, 126, 127, 130, 131, 132, 133, 136, 137, 138, 141, 142, 143, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 162, 163, 164, 172, 173, 186, 187, 189, 196, 202, 216, 217, 220, 228, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 253, 256, 257, 258, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 283, 284, 285, 287, 288, 289, 290, 291, 292, 293, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 344, 345, 346, 347, 362, 372, 379, 382, 385, 388, 393, 396, 399, 402, 421, 434, 458, 473], "summary": {"covered_lines": 217, "num_statements": 217, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [30, 31], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\data\\_odata.py": {"executed_lines": [6, 8, 10, 11, 12, 13, 14, 16, 18, 19, 20, 21, 22, 23, 36, 46, 49, 70, 71, 72, 80, 86, 87, 88, 90, 92, 93, 94, 103, 104, 105, 106, 107, 108, 109, 111, 112, 114, 115, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 140, 141, 142, 143, 144, 145, 146, 147, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 179, 185, 186, 187, 188, 189, 190, 193, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 225, 241, 242, 243, 244, 245, 246, 247, 248, 249, 251, 252, 253, 255, 256, 258, 259, 260, 262, 263, 264, 265, 266, 267, 269, 295, 296, 297, 298, 299, 301, 332, 333, 336, 337, 338, 339, 340, 341, 342, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 357, 359, 360, 361, 362, 364, 365, 366, 367, 368, 372, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 409, 424, 425, 426, 427, 431, 432, 433, 434, 435, 436, 437, 438, 440, 452, 454, 472, 473, 474, 475, 477, 488, 490, 503, 505, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 553, 554, 555, 556, 557, 558, 559, 561, 562, 563, 564, 566, 567, 569, 570, 572, 573, 575, 576, 577, 578, 579, 581, 582, 583, 584, 586, 587, 588, 590, 591, 592, 593, 594, 595, 598, 617, 618, 619, 620, 621, 626, 628, 629, 630, 631, 632, 635, 636, 637, 638, 639, 641, 642, 643, 646, 647, 648, 649, 650, 652, 653, 662, 663, 667, 668, 669, 670, 679, 680, 681, 682, 683, 684, 691, 692, 693, 694, 695, 702, 703, 704, 705, 706, 707, 708, 709, 710, 712, 715, 720, 721, 724, 725, 726, 727, 728, 730, 731, 732, 736, 737, 738, 739, 740, 741, 742, 743, 748, 752, 753, 754, 755, 759, 760, 761, 762, 763, 766, 777, 779, 780, 781, 785, 786, 787, 789, 796, 797, 809, 810, 811, 812, 813, 817, 818, 821, 822, 823, 825, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843, 844, 845, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859, 861, 889, 890, 891, 895, 896, 897, 898, 899, 900, 901, 902, 903, 905, 918, 919, 920, 921, 923, 924, 925, 926, 927, 928, 929, 930, 931, 934, 939, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 954, 962, 963, 964, 965, 966, 968, 969, 974, 975, 976, 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, 990, 992, 993, 995, 996, 997, 998, 999, 1000, 1001, 1002, 1003, 1004, 1006, 1008, 1017, 1020, 1024, 1025, 1028, 1031, 1032, 1033, 1035, 1037, 1038, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1049, 1050, 1052, 1061, 1062, 1063, 1064, 1074, 1101, 1103, 1115, 1116, 1117, 1121, 1125, 1152, 1153, 1154, 1159, 1160, 1161, 1165, 1166, 1167, 1168, 1170, 1176, 1190, 1191, 1192, 1197, 1198, 1199, 1200, 1202, 1218, 1219, 1220, 1225, 1226, 1227, 1229, 1259, 1260, 1261, 1266, 1270, 1271, 1273, 1277, 1278, 1279, 1280, 1281, 1282, 1283, 1284, 1286, 1287, 1288, 1289, 1290, 1292, 1293, 1294, 1296, 1303, 1313, 1333, 1334, 1336, 1337, 1338, 1343, 1344, 1345, 1347, 1348, 1349, 1350, 1354, 1355, 1356, 1361, 1362, 1364, 1365, 1367, 1369, 1390, 1391, 1392, 1393, 1395, 1397, 1398, 1399, 1401, 1402, 1403, 1409, 1410, 1411, 1412, 1414, 1415, 1416, 1417, 1422, 1423, 1424, 1426, 1428, 1429, 1430, 1431, 1432, 1434, 1436, 1437, 1439, 1443, 1452, 1453, 1454, 1461, 1468, 1470, 1471, 1472, 1473, 1474, 1475, 1476, 1477, 1478, 1484, 1497, 1498, 1499, 1502, 1503, 1504, 1512, 1524, 1525, 1526, 1527, 1528, 1529, 1530, 1531, 1532, 1538, 1560, 1582, 1590, 1591, 1595, 1596, 1597, 1598, 1599, 1600, 1601, 1604, 1605, 1609, 1610, 1611, 1612, 1613, 1614, 1620, 1631, 1634, 1635, 1636, 1643, 1645, 1646, 1647, 1648, 1679, 1685, 1693, 1694, 1695, 1696, 1697, 1699, 1714, 1715, 1716], "summary": {"covered_lines": 670, "num_statements": 690, "percent_covered": 97.10144927536231, "percent_covered_display": "97", "missing_lines": 20, "excluded_lines": 0, "percent_statements_covered": 97.10144927536231, "percent_statements_covered_display": "97"}, "missing_lines": [991, 994, 1034, 1469, 1500, 1546, 1547, 1548, 1549, 1550, 1551, 1555, 1557, 1558, 1572, 1573, 1574, 1575, 1576, 1632], "excluded_lines": [], "functions": {"_ODataClient.__init__": {"executed_lines": [70, 71, 72], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 49}, "_ODataClient.close": {"executed_lines": [86, 87, 88], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 80}, "_ODataClient._headers": {"executed_lines": [92, 93, 94], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 90}, "_ODataClient._merge_headers": {"executed_lines": [104, 105, 106, 107, 108, 109], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 103}, "_ODataClient._raw_request": {"executed_lines": [112], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 111}, "_ODataClient._request": {"executed_lines": [115, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 140, 141, 142, 143, 144, 145, 146, 147, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161], "summary": {"covered_lines": 35, "num_statements": 35, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 114}, "_ODataClient._execute_raw": {"executed_lines": [185, 186, 187, 188, 189, 190], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 179}, "_ODataClient._create": {"executed_lines": [209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 193}, "_ODataClient._create_multiple": {"executed_lines": [241, 242, 243, 244, 245, 246, 247, 248, 249, 251, 252, 253, 255, 256, 258, 259, 260, 262, 263, 264, 265, 266, 267], "summary": {"covered_lines": 23, "num_statements": 23, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 225}, "_ODataClient._upsert": {"executed_lines": [295, 296, 297, 298, 299], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 269}, "_ODataClient._upsert_multiple": {"executed_lines": [332, 333, 336, 337, 338, 339, 340, 341, 342, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354], "summary": {"covered_lines": 19, "num_statements": 19, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 301}, "_ODataClient._primary_id_attr": {"executed_lines": [359, 360, 361, 362, 364, 365, 366, 367, 368], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 357}, "_ODataClient._update_by_ids": {"executed_lines": [387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407], "summary": {"covered_lines": 21, "num_statements": 21, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 372}, "_ODataClient._delete_multiple": {"executed_lines": [424, 425, 426, 427, 431, 432, 433, 434, 435, 436, 437, 438], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 409}, "_ODataClient._update": {"executed_lines": [452], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 440}, "_ODataClient._update_multiple": {"executed_lines": [472, 473, 474, 475], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 454}, "_ODataClient._delete": {"executed_lines": [488], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 477}, "_ODataClient._get": {"executed_lines": [503], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 490}, "_ODataClient._get_multiple": {"executed_lines": [542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 553, 561, 562, 563, 564, 566, 567, 569, 570, 572, 573, 575, 576, 577, 578, 579, 581, 582, 583, 584, 586, 587, 588, 590, 591, 592, 593, 594, 595], "summary": {"covered_lines": 39, "num_statements": 39, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 505}, "_ODataClient._get_multiple._do_request": {"executed_lines": [554, 555, 556, 557, 558, 559], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 553}, "_ODataClient._query_sql": {"executed_lines": [617, 618, 619, 620, 621, 626, 628, 629, 630, 631, 632, 635, 636, 637, 638, 639, 641, 642, 643, 646, 647, 648, 649, 650, 652, 653, 662, 663, 667, 668, 669, 670, 679, 680, 681, 682, 683, 684, 691, 692, 693, 694, 695, 702, 703, 704, 705, 706, 707, 708, 709, 710, 712], "summary": {"covered_lines": 53, "num_statements": 53, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 598}, "_ODataClient._entity_set_from_schema_name": {"executed_lines": [720, 721, 724, 725, 726, 727, 728, 730, 731, 732, 736, 737, 738, 739, 740, 741, 742, 743, 748, 752, 753, 754, 755, 759, 760, 761, 762, 763], "summary": {"covered_lines": 28, "num_statements": 28, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 715}, "_ODataClient._get_entity_by_table_schema_name": {"executed_lines": [777, 779, 780, 781, 785, 786, 787], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 766}, "_ODataClient._create_entity": {"executed_lines": [796, 797, 809, 810, 811, 812, 813, 817, 818, 821, 822, 823], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 789}, "_ODataClient._get_attribute_metadata": {"executed_lines": [832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843, 844, 845, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859], "summary": {"covered_lines": 25, "num_statements": 25, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 825}, "_ODataClient._list_columns": {"executed_lines": [889, 890, 891, 895, 896, 897, 898, 899, 900, 901, 902, 903], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 861}, "_ODataClient._wait_for_attribute_visibility": {"executed_lines": [918, 919, 920, 921, 923, 924, 925, 926, 927, 928, 929, 930, 931, 934], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 905}, "_ODataClient._request_metadata_with_retry": {"executed_lines": [941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 939}, "_ODataClient._bulk_fetch_picklists": {"executed_lines": [962, 963, 964, 965, 966, 968, 969, 974, 975, 976, 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, 990, 992, 993, 995, 996, 997, 998, 999, 1000, 1001, 1002, 1003, 1004, 1006], "summary": {"covered_lines": 36, "num_statements": 38, "percent_covered": 94.73684210526316, "percent_covered_display": "95", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 94.73684210526316, "percent_statements_covered_display": "95"}, "missing_lines": [991, 994], "excluded_lines": [], "start_line": 954}, "_ODataClient._convert_labels_to_ints": {"executed_lines": [1017, 1020, 1024, 1025, 1028, 1031, 1032, 1033, 1035, 1037, 1038, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1049, 1050], "summary": {"covered_lines": 23, "num_statements": 24, "percent_covered": 95.83333333333333, "percent_covered_display": "96", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 95.83333333333333, "percent_statements_covered_display": "96"}, "missing_lines": [1034], "excluded_lines": [], "start_line": 1008}, "_ODataClient._get_table_info": {"executed_lines": [1061, 1062, 1063, 1064], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1052}, "_ODataClient._list_tables": {"executed_lines": [1101], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1074}, "_ODataClient._delete_table": {"executed_lines": [1115, 1116, 1117, 1121], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1103}, "_ODataClient._create_alternate_key": {"executed_lines": [1152, 1153, 1154, 1159, 1160, 1161, 1165, 1166, 1167, 1168, 1170], "summary": {"covered_lines": 11, "num_statements": 11, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1125}, "_ODataClient._get_alternate_keys": {"executed_lines": [1190, 1191, 1192, 1197, 1198, 1199, 1200], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1176}, "_ODataClient._delete_alternate_key": {"executed_lines": [1218, 1219, 1220, 1225, 1226, 1227], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1202}, "_ODataClient._create_table": {"executed_lines": [1259, 1260, 1261, 1266, 1270, 1271, 1273, 1277, 1278, 1279, 1280, 1281, 1282, 1283, 1284, 1286, 1287, 1288, 1289, 1290, 1292, 1293, 1294, 1296, 1303], "summary": {"covered_lines": 25, "num_statements": 25, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1229}, "_ODataClient._create_columns": {"executed_lines": [1333, 1334, 1336, 1337, 1338, 1343, 1344, 1345, 1347, 1348, 1349, 1350, 1354, 1355, 1356, 1361, 1362, 1364, 1365, 1367], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1313}, "_ODataClient._delete_columns": {"executed_lines": [1390, 1391, 1392, 1393, 1395, 1397, 1398, 1399, 1401, 1402, 1403, 1409, 1410, 1411, 1412, 1414, 1415, 1416, 1417, 1422, 1423, 1424, 1426, 1428, 1429, 1430, 1431, 1432, 1434, 1436, 1437, 1439], "summary": {"covered_lines": 32, "num_statements": 32, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1369}, "_ODataClient._build_create": {"executed_lines": [1452, 1453, 1454], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1443}, "_ODataClient._build_create_multiple": {"executed_lines": [1468, 1470, 1471, 1472, 1473, 1474, 1475, 1476, 1477, 1478], "summary": {"covered_lines": 10, "num_statements": 11, "percent_covered": 90.9090909090909, "percent_covered_display": "91", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 90.9090909090909, "percent_statements_covered_display": "91"}, "missing_lines": [1469], "excluded_lines": [], "start_line": 1461}, "_ODataClient._build_update": {"executed_lines": [1497, 1498, 1499, 1502, 1503, 1504], "summary": {"covered_lines": 6, "num_statements": 7, "percent_covered": 85.71428571428571, "percent_covered_display": "86", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 85.71428571428571, "percent_statements_covered_display": "86"}, "missing_lines": [1500], "excluded_lines": [], "start_line": 1484}, "_ODataClient._build_update_multiple_from_records": {"executed_lines": [1524, 1525, 1526, 1527, 1528, 1529, 1530, 1531, 1532], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1512}, "_ODataClient._build_update_multiple": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 9, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 9, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1546, 1547, 1548, 1549, 1550, 1551, 1555, 1557, 1558], "excluded_lines": [], "start_line": 1538}, "_ODataClient._build_upsert": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 5, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 5, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1572, 1573, 1574, 1575, 1576], "excluded_lines": [], "start_line": 1560}, "_ODataClient._build_upsert_multiple": {"executed_lines": [1590, 1591, 1595, 1596, 1597, 1598, 1599, 1600, 1601, 1604, 1605, 1609, 1610, 1611, 1612, 1613, 1614], "summary": {"covered_lines": 17, "num_statements": 17, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1582}, "_ODataClient._build_delete": {"executed_lines": [1631, 1634, 1635, 1636], "summary": {"covered_lines": 4, "num_statements": 5, "percent_covered": 80.0, "percent_covered_display": "80", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 80.0, "percent_statements_covered_display": "80"}, "missing_lines": [1632], "excluded_lines": [], "start_line": 1620}, "_ODataClient._build_delete_multiple": {"executed_lines": [1645, 1646, 1647, 1648, 1679], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1643}, "_ODataClient._build_get": {"executed_lines": [1693, 1694, 1695, 1696, 1697], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1685}, "_ODataClient._build_sql": {"executed_lines": [1714, 1715, 1716], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1699}, "": {"executed_lines": [6, 8, 10, 11, 12, 13, 14, 16, 18, 19, 20, 21, 22, 23, 36, 46, 49, 80, 90, 103, 111, 114, 179, 193, 225, 269, 301, 357, 372, 409, 440, 454, 477, 490, 505, 598, 715, 766, 789, 825, 861, 905, 939, 954, 1008, 1052, 1074, 1103, 1125, 1176, 1202, 1229, 1313, 1369, 1443, 1461, 1484, 1512, 1538, 1560, 1582, 1620, 1643, 1685, 1699], "summary": {"covered_lines": 65, "num_statements": 65, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_ODataClient": {"executed_lines": [70, 71, 72, 86, 87, 88, 92, 93, 94, 104, 105, 106, 107, 108, 109, 112, 115, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 140, 141, 142, 143, 144, 145, 146, 147, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 185, 186, 187, 188, 189, 190, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 241, 242, 243, 244, 245, 246, 247, 248, 249, 251, 252, 253, 255, 256, 258, 259, 260, 262, 263, 264, 265, 266, 267, 295, 296, 297, 298, 299, 332, 333, 336, 337, 338, 339, 340, 341, 342, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 359, 360, 361, 362, 364, 365, 366, 367, 368, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 424, 425, 426, 427, 431, 432, 433, 434, 435, 436, 437, 438, 452, 472, 473, 474, 475, 488, 503, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 553, 554, 555, 556, 557, 558, 559, 561, 562, 563, 564, 566, 567, 569, 570, 572, 573, 575, 576, 577, 578, 579, 581, 582, 583, 584, 586, 587, 588, 590, 591, 592, 593, 594, 595, 617, 618, 619, 620, 621, 626, 628, 629, 630, 631, 632, 635, 636, 637, 638, 639, 641, 642, 643, 646, 647, 648, 649, 650, 652, 653, 662, 663, 667, 668, 669, 670, 679, 680, 681, 682, 683, 684, 691, 692, 693, 694, 695, 702, 703, 704, 705, 706, 707, 708, 709, 710, 712, 720, 721, 724, 725, 726, 727, 728, 730, 731, 732, 736, 737, 738, 739, 740, 741, 742, 743, 748, 752, 753, 754, 755, 759, 760, 761, 762, 763, 777, 779, 780, 781, 785, 786, 787, 796, 797, 809, 810, 811, 812, 813, 817, 818, 821, 822, 823, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843, 844, 845, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859, 889, 890, 891, 895, 896, 897, 898, 899, 900, 901, 902, 903, 918, 919, 920, 921, 923, 924, 925, 926, 927, 928, 929, 930, 931, 934, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 962, 963, 964, 965, 966, 968, 969, 974, 975, 976, 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, 990, 992, 993, 995, 996, 997, 998, 999, 1000, 1001, 1002, 1003, 1004, 1006, 1017, 1020, 1024, 1025, 1028, 1031, 1032, 1033, 1035, 1037, 1038, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1049, 1050, 1061, 1062, 1063, 1064, 1101, 1115, 1116, 1117, 1121, 1152, 1153, 1154, 1159, 1160, 1161, 1165, 1166, 1167, 1168, 1170, 1190, 1191, 1192, 1197, 1198, 1199, 1200, 1218, 1219, 1220, 1225, 1226, 1227, 1259, 1260, 1261, 1266, 1270, 1271, 1273, 1277, 1278, 1279, 1280, 1281, 1282, 1283, 1284, 1286, 1287, 1288, 1289, 1290, 1292, 1293, 1294, 1296, 1303, 1333, 1334, 1336, 1337, 1338, 1343, 1344, 1345, 1347, 1348, 1349, 1350, 1354, 1355, 1356, 1361, 1362, 1364, 1365, 1367, 1390, 1391, 1392, 1393, 1395, 1397, 1398, 1399, 1401, 1402, 1403, 1409, 1410, 1411, 1412, 1414, 1415, 1416, 1417, 1422, 1423, 1424, 1426, 1428, 1429, 1430, 1431, 1432, 1434, 1436, 1437, 1439, 1452, 1453, 1454, 1468, 1470, 1471, 1472, 1473, 1474, 1475, 1476, 1477, 1478, 1497, 1498, 1499, 1502, 1503, 1504, 1524, 1525, 1526, 1527, 1528, 1529, 1530, 1531, 1532, 1590, 1591, 1595, 1596, 1597, 1598, 1599, 1600, 1601, 1604, 1605, 1609, 1610, 1611, 1612, 1613, 1614, 1631, 1634, 1635, 1636, 1645, 1646, 1647, 1648, 1679, 1693, 1694, 1695, 1696, 1697, 1714, 1715, 1716], "summary": {"covered_lines": 605, "num_statements": 625, "percent_covered": 96.8, "percent_covered_display": "97", "missing_lines": 20, "excluded_lines": 0, "percent_statements_covered": 96.8, "percent_statements_covered_display": "97"}, "missing_lines": [991, 994, 1034, 1469, 1500, 1546, 1547, 1548, 1549, 1550, 1551, 1555, 1557, 1558, 1572, 1573, 1574, 1575, 1576, 1632], "excluded_lines": [], "start_line": 46}, "": {"executed_lines": [6, 8, 10, 11, 12, 13, 14, 16, 18, 19, 20, 21, 22, 23, 36, 46, 49, 80, 90, 103, 111, 114, 179, 193, 225, 269, 301, 357, 372, 409, 440, 454, 477, 490, 505, 598, 715, 766, 789, 825, 861, 905, 939, 954, 1008, 1052, 1074, 1103, 1125, 1176, 1202, 1229, 1313, 1369, 1443, 1461, 1484, 1512, 1538, 1560, 1582, 1620, 1643, 1685, 1699], "summary": {"covered_lines": 65, "num_statements": 65, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\data\\_odata_base.py": {"executed_lines": [10, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24, 26, 27, 33, 38, 39, 40, 42, 44, 45, 46, 47, 50, 60, 61, 62, 63, 64, 70, 71, 72, 73, 74, 75, 78, 79, 82, 83, 84, 85, 86, 88, 89, 98, 99, 100, 101, 102, 103, 112, 120, 130, 131, 133, 134, 140, 142, 143, 144, 145, 146, 155, 156, 158, 160, 161, 163, 165, 166, 179, 180, 181, 183, 184, 189, 190, 191, 193, 194, 202, 203, 205, 206, 207, 208, 209, 210, 216, 217, 219, 220, 221, 222, 224, 226, 227, 228, 229, 231, 233, 235, 237, 238, 239, 240, 241, 243, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 270, 271, 273, 274, 275, 286, 287, 288, 290, 292, 295, 296, 298, 299, 305, 310, 311, 312, 313, 314, 315, 316, 323, 324, 325, 330, 343, 344, 345, 348, 349, 350, 352, 353, 356, 358, 360, 361, 362, 364, 365, 366, 367, 368, 370, 371, 372, 373, 374, 375, 376, 377, 379, 380, 381, 382, 383, 384, 386, 387, 388, 389, 391, 393, 395, 396, 397, 398, 399, 400, 401, 409, 410, 423, 427, 428, 429, 430, 433, 434, 435, 436, 445, 446, 455, 456, 465, 466, 475, 476, 485, 486, 494, 495, 513, 514, 520, 526, 535, 536, 538, 539, 540, 541, 542, 543, 547, 548, 549, 550, 551, 552, 564, 565, 566, 567, 573, 575, 581, 583, 584, 593, 600, 601, 602, 604, 605, 606, 607, 608, 609, 610, 612, 631, 637, 643, 644, 668, 669, 671, 683, 684, 687, 688, 695, 697, 714, 722, 735, 739, 740, 741, 746, 750, 751, 752, 753, 761, 766, 800, 801, 802, 810, 811, 812, 820, 821, 829, 830, 837, 838, 845, 846, 858, 859, 869, 870, 880, 881, 891, 897, 902, 903, 904, 905, 913, 925, 926, 927, 932, 933, 934], "summary": {"covered_lines": 312, "num_statements": 329, "percent_covered": 94.83282674772036, "percent_covered_display": "95", "missing_lines": 17, "excluded_lines": 0, "percent_statements_covered": 94.83282674772036, "percent_statements_covered_display": "95"}, "missing_lines": [132, 147, 149, 293, 619, 620, 621, 625, 704, 705, 706, 707, 716, 724, 725, 906, 907], "excluded_lines": [], "functions": {"_extract_pagingcookie": {"executed_lines": [60, 61, 62, 63, 64, 70, 71, 72, 73, 74, 75], "summary": {"covered_lines": 11, "num_statements": 11, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 50}, "_RequestContext.build": {"executed_lines": [98, 99, 100, 101, 102, 103], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 89}, "_ODataBase.__init__": {"executed_lines": [130, 131, 133, 134, 140, 142, 143, 144, 145, 146], "summary": {"covered_lines": 10, "num_statements": 13, "percent_covered": 76.92307692307692, "percent_covered_display": "77", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 76.92307692307692, "percent_statements_covered_display": "77"}, "missing_lines": [132, 147, 149], "excluded_lines": [], "start_line": 120}, "_ODataBase._escape_odata_quotes": {"executed_lines": [158], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 156}, "_ODataBase._normalize_cache_key": {"executed_lines": [163], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 161}, "_ODataBase._lowercase_keys": {"executed_lines": [179, 180, 181], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 166}, "_ODataBase._lowercase_list": {"executed_lines": [189, 190, 191], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 184}, "_ODataBase._extract_logical_table": {"executed_lines": [202, 203, 205, 206, 207, 208, 209, 210], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 194}, "_ODataBase._call_scope": {"executed_lines": [219, 220, 221, 222, 224], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 217}, "_ODataBase._format_key": {"executed_lines": [227, 228, 229, 231, 233, 237, 238, 239, 240, 241], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 226}, "_ODataBase._format_key.esc": {"executed_lines": [235], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 233}, "_ODataBase._build_alternate_key_str": {"executed_lines": [258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 270, 271], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 243}, "_ODataBase._label": {"executed_lines": [274, 275], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 273}, "_ODataBase._to_pascal": {"executed_lines": [287, 288], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 286}, "_ODataBase._normalize_picklist_label": {"executed_lines": [292, 295, 296, 298, 299], "summary": {"covered_lines": 5, "num_statements": 6, "percent_covered": 83.33333333333333, "percent_covered_display": "83", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 83.33333333333333, "percent_statements_covered_display": "83"}, "missing_lines": [293], "excluded_lines": [], "start_line": 290}, "_ODataBase._build_localizedlabels_payload": {"executed_lines": [310, 311, 312, 313, 314, 315, 316, 323, 324, 325], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 305}, "_ODataBase._enum_optionset_payload": {"executed_lines": [343, 344, 345, 348, 349, 350, 352, 353, 356, 358, 360, 361, 362, 364, 365, 366, 367, 368, 370, 371, 372, 373, 374, 375, 376, 377, 379, 380, 381, 382, 383, 384, 386, 387, 388, 389, 391, 393, 395, 396, 397, 398, 399, 400, 401, 409, 410], "summary": {"covered_lines": 47, "num_statements": 47, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 330}, "_ODataBase._attribute_payload": {"executed_lines": [427, 428, 429, 430, 433, 434, 435, 436, 445, 446, 455, 456, 465, 466, 475, 476, 485, 486, 494, 495, 513, 514, 520], "summary": {"covered_lines": 23, "num_statements": 23, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 423}, "_ODataBase._build_create_entity": {"executed_lines": [535, 536, 538, 539, 540, 541, 542, 543, 547, 548, 549, 550, 551, 552, 564, 565, 566, 567], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 526}, "_ODataBase._build_delete_entity": {"executed_lines": [575], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 573}, "_ODataBase._build_get_entity": {"executed_lines": [583, 584], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 581}, "_ODataBase._build_list_entities": {"executed_lines": [600, 601, 602, 604, 605, 606, 607, 608, 609, 610], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 593}, "_ODataBase._build_create_column": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 4, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [619, 620, 621, 625], "excluded_lines": [], "start_line": 612}, "_ODataBase._build_delete_column": {"executed_lines": [637], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 631}, "_ODataBase._build_lookup_field_models": {"executed_lines": [668, 669, 671, 683, 684, 687, 688, 695], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 644}, "_ODataBase._build_create_relationship": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 4, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [704, 705, 706, 707], "excluded_lines": [], "start_line": 697}, "_ODataBase._build_delete_relationship": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [716], "excluded_lines": [], "start_line": 714}, "_ODataBase._build_get_relationship": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [724, 725], "excluded_lines": [], "start_line": 722}, "_ODataBase._sql_guardrails": {"executed_lines": [800, 801, 802, 810, 811, 812, 820, 821, 829, 830, 837, 838, 845, 846, 858, 859, 869, 870, 880, 881, 891], "summary": {"covered_lines": 21, "num_statements": 21, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 766}, "_ODataBase.close": {"executed_lines": [902, 903, 904, 905], "summary": {"covered_lines": 4, "num_statements": 6, "percent_covered": 66.66666666666667, "percent_covered_display": "67", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 66.66666666666667, "percent_statements_covered_display": "67"}, "missing_lines": [906, 907], "excluded_lines": [], "start_line": 897}, "_ODataBase._flush_cache": {"executed_lines": [925, 926, 927, 932, 933, 934], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 913}, "": {"executed_lines": [10, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24, 26, 27, 33, 38, 39, 40, 42, 44, 45, 46, 47, 50, 78, 79, 82, 83, 84, 85, 86, 88, 89, 112, 120, 155, 156, 160, 161, 165, 166, 183, 184, 193, 194, 216, 217, 226, 243, 273, 286, 290, 305, 330, 423, 526, 573, 581, 593, 612, 631, 643, 644, 697, 714, 722, 735, 739, 740, 741, 746, 750, 751, 752, 753, 761, 766, 897, 913], "summary": {"covered_lines": 80, "num_statements": 80, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_RequestContext": {"executed_lines": [98, 99, 100, 101, 102, 103], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 79}, "_ODataBase": {"executed_lines": [130, 131, 133, 134, 140, 142, 143, 144, 145, 146, 158, 163, 179, 180, 181, 189, 190, 191, 202, 203, 205, 206, 207, 208, 209, 210, 219, 220, 221, 222, 224, 227, 228, 229, 231, 233, 235, 237, 238, 239, 240, 241, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 270, 271, 274, 275, 287, 288, 292, 295, 296, 298, 299, 310, 311, 312, 313, 314, 315, 316, 323, 324, 325, 343, 344, 345, 348, 349, 350, 352, 353, 356, 358, 360, 361, 362, 364, 365, 366, 367, 368, 370, 371, 372, 373, 374, 375, 376, 377, 379, 380, 381, 382, 383, 384, 386, 387, 388, 389, 391, 393, 395, 396, 397, 398, 399, 400, 401, 409, 410, 427, 428, 429, 430, 433, 434, 435, 436, 445, 446, 455, 456, 465, 466, 475, 476, 485, 486, 494, 495, 513, 514, 520, 535, 536, 538, 539, 540, 541, 542, 543, 547, 548, 549, 550, 551, 552, 564, 565, 566, 567, 575, 583, 584, 600, 601, 602, 604, 605, 606, 607, 608, 609, 610, 637, 668, 669, 671, 683, 684, 687, 688, 695, 800, 801, 802, 810, 811, 812, 820, 821, 829, 830, 837, 838, 845, 846, 858, 859, 869, 870, 880, 881, 891, 902, 903, 904, 905, 925, 926, 927, 932, 933, 934], "summary": {"covered_lines": 215, "num_statements": 232, "percent_covered": 92.67241379310344, "percent_covered_display": "93", "missing_lines": 17, "excluded_lines": 0, "percent_statements_covered": 92.67241379310344, "percent_statements_covered_display": "93"}, "missing_lines": [132, 147, 149, 293, 619, 620, 621, 625, 704, 705, 706, 707, 716, 724, 725, 906, 907], "excluded_lines": [], "start_line": 112}, "": {"executed_lines": [10, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24, 26, 27, 33, 38, 39, 40, 42, 44, 45, 46, 47, 50, 60, 61, 62, 63, 64, 70, 71, 72, 73, 74, 75, 78, 79, 82, 83, 84, 85, 86, 88, 89, 112, 120, 155, 156, 160, 161, 165, 166, 183, 184, 193, 194, 216, 217, 226, 243, 273, 286, 290, 305, 330, 423, 526, 573, 581, 593, 612, 631, 643, 644, 697, 714, 722, 735, 739, 740, 741, 746, 750, 751, 752, 753, 761, 766, 897, 913], "summary": {"covered_lines": 91, "num_statements": 91, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\data\\_raw_request.py": {"executed_lines": [6, 8, 9, 11, 14, 15, 29, 30, 31, 32, 33], "summary": {"covered_lines": 11, "num_statements": 11, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [6, 8, 9, 11, 14, 15, 29, 30, 31, 32, 33], "summary": {"covered_lines": 11, "num_statements": 11, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_RawRequest": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 15}, "": {"executed_lines": [6, 8, 9, 11, 14, 15, 29, 30, 31, 32, 33], "summary": {"covered_lines": 11, "num_statements": 11, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\data\\_relationships.py": {"executed_lines": [10, 12, 14, 15, 18, 28, 51, 54, 55, 57, 58, 59, 61, 64, 66, 74, 94, 96, 98, 99, 100, 102, 105, 107, 114, 123, 124, 125, 126, 128, 140, 141, 142, 143, 144, 145, 147, 171, 172, 173, 174, 175, 176, 177, 178, 180, 210, 211, 213, 214, 215, 220, 228, 229, 230, 231, 232, 233, 234, 236, 237, 238, 240, 241, 242, 244, 246, 256, 257, 258, 259], "summary": {"covered_lines": 71, "num_statements": 71, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"_RelationshipOperationsMixin._create_one_to_many_relationship": {"executed_lines": [51, 54, 55, 57, 58, 59, 61, 64, 66], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 28}, "_RelationshipOperationsMixin._create_many_to_many_relationship": {"executed_lines": [94, 96, 98, 99, 100, 102, 105, 107], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 74}, "_RelationshipOperationsMixin._delete_relationship": {"executed_lines": [123, 124, 125, 126], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 114}, "_RelationshipOperationsMixin._get_relationship": {"executed_lines": [140, 141, 142, 143, 144, 145], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 128}, "_RelationshipOperationsMixin._list_relationships": {"executed_lines": [171, 172, 173, 174, 175, 176, 177, 178], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 147}, "_RelationshipOperationsMixin._list_table_relationships": {"executed_lines": [210, 211, 213, 214, 215, 220, 228, 229, 230, 231, 232, 233, 234, 236, 237, 238, 240, 241, 242, 244], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 180}, "_RelationshipOperationsMixin._extract_id_from_header": {"executed_lines": [256, 257, 258, 259], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 246}, "": {"executed_lines": [10, 12, 14, 15, 18, 28, 74, 114, 128, 147, 180, 246], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_RelationshipOperationsMixin": {"executed_lines": [51, 54, 55, 57, 58, 59, 61, 64, 66, 94, 96, 98, 99, 100, 102, 105, 107, 123, 124, 125, 126, 140, 141, 142, 143, 144, 145, 171, 172, 173, 174, 175, 176, 177, 178, 210, 211, 213, 214, 215, 220, 228, 229, 230, 231, 232, 233, 234, 236, 237, 238, 240, 241, 242, 244, 256, 257, 258, 259], "summary": {"covered_lines": 59, "num_statements": 59, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 18}, "": {"executed_lines": [10, 12, 14, 15, 18, 28, 74, 114, 128, 147, 180, 246], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\data\\_upload.py": {"executed_lines": [6, 8, 11, 14, 43, 46, 49, 50, 51, 52, 53, 54, 56, 59, 61, 63, 64, 65, 66, 67, 70, 72, 73, 76, 77, 78, 80, 90, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 109, 110, 112, 114, 115, 117, 147, 148, 150, 151, 152, 153, 154, 155, 156, 157, 158, 161, 162, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 193, 194, 195], "summary": {"covered_lines": 87, "num_statements": 87, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"_FileUploadMixin._upload_file": {"executed_lines": [43, 46, 49, 50, 51, 52, 53, 54, 56, 59, 61, 63, 64, 65, 66, 67, 70, 72, 73, 76, 77, 78], "summary": {"covered_lines": 22, "num_statements": 22, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 14}, "_FileUploadMixin._upload_file_small": {"executed_lines": [90, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 109, 110, 112, 114, 115], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 80}, "_FileUploadMixin._upload_file_chunk": {"executed_lines": [147, 148, 150, 151, 152, 153, 154, 155, 156, 157, 158, 161, 162, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 193, 194, 195], "summary": {"covered_lines": 39, "num_statements": 39, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 117}, "": {"executed_lines": [6, 8, 11, 14, 80, 117], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_FileUploadMixin": {"executed_lines": [43, 46, 49, 50, 51, 52, 53, 54, 56, 59, 61, 63, 64, 65, 66, 67, 70, 72, 73, 76, 77, 78, 90, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 109, 110, 112, 114, 115, 147, 148, 150, 151, 152, 153, 154, 155, 156, 157, 158, 161, 162, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 193, 194, 195], "summary": {"covered_lines": 81, "num_statements": 81, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 11}, "": {"executed_lines": [6, 8, 11, 14, 80, 117], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\extensions\\__init__.py": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [9], "excluded_lines": [], "functions": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [9], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [9], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\models\\__init__.py": {"executed_lines": [19], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [19], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [19], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\models\\batch.py": {"executed_lines": [6, 8, 9, 11, 14, 15, 39, 40, 41, 42, 43, 44, 46, 47, 49, 52, 53, 71, 73, 74, 76, 78, 79, 81, 83, 84, 86, 88, 89, 106], "summary": {"covered_lines": 30, "num_statements": 30, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"BatchItemResponse.is_success": {"executed_lines": [49], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 47}, "BatchResult.succeeded": {"executed_lines": [76], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 74}, "BatchResult.failed": {"executed_lines": [81], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 79}, "BatchResult.has_errors": {"executed_lines": [86], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 84}, "BatchResult.entity_ids": {"executed_lines": [106], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 89}, "": {"executed_lines": [6, 8, 9, 11, 14, 15, 39, 40, 41, 42, 43, 44, 46, 47, 52, 53, 71, 73, 74, 78, 79, 83, 84, 88, 89], "summary": {"covered_lines": 25, "num_statements": 25, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"BatchItemResponse": {"executed_lines": [49], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 15}, "BatchResult": {"executed_lines": [76, 81, 86, 106], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 53}, "": {"executed_lines": [6, 8, 9, 11, 14, 15, 39, 40, 41, 42, 43, 44, 46, 47, 52, 53, 71, 73, 74, 78, 79, 83, 84, 88, 89], "summary": {"covered_lines": 25, "num_statements": 25, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\models\\filters.py": {"executed_lines": [34, 36, 37, 38, 39, 41, 67, 78, 79, 81, 82, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 95, 96, 97, 98, 99, 100, 101, 102, 103, 105, 113, 123, 125, 127, 128, 129, 130, 132, 133, 134, 135, 137, 138, 140, 141, 143, 144, 152, 155, 157, 158, 159, 160, 162, 163, 166, 169, 171, 172, 173, 174, 176, 177, 180, 183, 185, 186, 187, 189, 190, 193, 196, 198, 199, 200, 202, 203, 206, 209, 211, 212, 214, 215, 218, 221, 223, 224, 225, 226, 227, 229, 231, 232, 233, 236, 239, 241, 242, 243, 244, 245, 247, 249, 250, 251, 254, 257, 259, 260, 262, 263, 271, 282, 285, 292, 295, 302, 305, 312, 315, 322, 325, 332, 335, 342, 345, 352, 355, 362, 365, 380, 383, 389, 392, 398, 401, 416, 419, 434, 437, 452, 455, 465], "summary": {"covered_lines": 144, "num_statements": 144, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"_format_value": {"executed_lines": [78, 79, 81, 82, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 95, 96, 97, 98, 99, 100, 101, 102, 103, 105], "summary": {"covered_lines": 24, "num_statements": 24, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 67}, "FilterExpression.to_odata": {"executed_lines": [125], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 123}, "FilterExpression.__and__": {"executed_lines": [128, 129, 130], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 127}, "FilterExpression.__or__": {"executed_lines": [133, 134, 135], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 132}, "FilterExpression.__invert__": {"executed_lines": [138], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 137}, "FilterExpression.__str__": {"executed_lines": [141], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 140}, "FilterExpression.__repr__": {"executed_lines": [144], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 143}, "_ComparisonFilter.__init__": {"executed_lines": [158, 159, 160], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 157}, "_ComparisonFilter.to_odata": {"executed_lines": [163], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 162}, "_FunctionFilter.__init__": {"executed_lines": [172, 173, 174], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 171}, "_FunctionFilter.to_odata": {"executed_lines": [177], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 176}, "_AndFilter.__init__": {"executed_lines": [186, 187], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 185}, "_AndFilter.to_odata": {"executed_lines": [190], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 189}, "_OrFilter.__init__": {"executed_lines": [199, 200], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 198}, "_OrFilter.to_odata": {"executed_lines": [203], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 202}, "_NotFilter.__init__": {"executed_lines": [212], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 211}, "_NotFilter.to_odata": {"executed_lines": [215], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 214}, "_InFilter.__init__": {"executed_lines": [224, 225, 226, 227], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 223}, "_InFilter.to_odata": {"executed_lines": [231, 232, 233], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 229}, "_NotInFilter.__init__": {"executed_lines": [242, 243, 244, 245], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 241}, "_NotInFilter.to_odata": {"executed_lines": [249, 250, 251], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 247}, "_RawFilter.__init__": {"executed_lines": [260], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 259}, "_RawFilter.to_odata": {"executed_lines": [263], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 262}, "eq": {"executed_lines": [282], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 271}, "ne": {"executed_lines": [292], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 285}, "gt": {"executed_lines": [302], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 295}, "ge": {"executed_lines": [312], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 305}, "lt": {"executed_lines": [322], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 315}, "le": {"executed_lines": [332], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 325}, "contains": {"executed_lines": [342], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 335}, "startswith": {"executed_lines": [352], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 345}, "endswith": {"executed_lines": [362], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 355}, "between": {"executed_lines": [380], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 365}, "is_null": {"executed_lines": [389], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 383}, "is_not_null": {"executed_lines": [398], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 392}, "filter_in": {"executed_lines": [416], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 401}, "not_in": {"executed_lines": [434], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 419}, "not_between": {"executed_lines": [452], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 437}, "raw": {"executed_lines": [465], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 455}, "": {"executed_lines": [34, 36, 37, 38, 39, 41, 67, 113, 123, 127, 132, 137, 140, 143, 152, 155, 157, 162, 166, 169, 171, 176, 180, 183, 185, 189, 193, 196, 198, 202, 206, 209, 211, 214, 218, 221, 223, 229, 236, 239, 241, 247, 254, 257, 259, 262, 271, 285, 295, 305, 315, 325, 335, 345, 355, 365, 383, 392, 401, 419, 437, 455], "summary": {"covered_lines": 62, "num_statements": 62, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"FilterExpression": {"executed_lines": [125, 128, 129, 130, 133, 134, 135, 138, 141, 144], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 113}, "_ComparisonFilter": {"executed_lines": [158, 159, 160, 163], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 152}, "_FunctionFilter": {"executed_lines": [172, 173, 174, 177], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 166}, "_AndFilter": {"executed_lines": [186, 187, 190], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 180}, "_OrFilter": {"executed_lines": [199, 200, 203], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 193}, "_NotFilter": {"executed_lines": [212, 215], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 206}, "_InFilter": {"executed_lines": [224, 225, 226, 227, 231, 232, 233], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 218}, "_NotInFilter": {"executed_lines": [242, 243, 244, 245, 249, 250, 251], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 236}, "_RawFilter": {"executed_lines": [260, 263], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 254}, "": {"executed_lines": [34, 36, 37, 38, 39, 41, 67, 78, 79, 81, 82, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 95, 96, 97, 98, 99, 100, 101, 102, 103, 105, 113, 123, 127, 132, 137, 140, 143, 152, 155, 157, 162, 166, 169, 171, 176, 180, 183, 185, 189, 193, 196, 198, 202, 206, 209, 211, 214, 218, 221, 223, 229, 236, 239, 241, 247, 254, 257, 259, 262, 271, 282, 285, 292, 295, 302, 305, 312, 315, 322, 325, 332, 335, 342, 345, 352, 355, 362, 365, 380, 383, 389, 392, 398, 401, 416, 419, 434, 437, 452, 455, 465], "summary": {"covered_lines": 102, "num_statements": 102, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\models\\labels.py": {"executed_lines": [6, 8, 9, 11, 17, 18, 31, 32, 33, 35, 49, 54, 55, 56, 59, 60, 73, 74, 75, 77, 93, 98, 99, 100, 101, 102, 103, 104, 107], "summary": {"covered_lines": 29, "num_statements": 29, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"LocalizedLabel.to_dict": {"executed_lines": [49, 54, 55, 56], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 35}, "Label.to_dict": {"executed_lines": [93, 98, 99, 100, 101, 102, 103, 104], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 77}, "": {"executed_lines": [6, 8, 9, 11, 17, 18, 31, 32, 33, 35, 59, 60, 73, 74, 75, 77, 107], "summary": {"covered_lines": 17, "num_statements": 17, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"LocalizedLabel": {"executed_lines": [49, 54, 55, 56], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 18}, "Label": {"executed_lines": [93, 98, 99, 100, 101, 102, 103, 104], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 60}, "": {"executed_lines": [6, 8, 9, 11, 17, 18, 31, 32, 33, 35, 59, 60, 73, 74, 75, 77, 107], "summary": {"covered_lines": 17, "num_statements": 17, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\models\\query_builder.py": {"executed_lines": [47, 49, 51, 53, 54, 56, 59, 66, 67, 68, 69, 70, 71, 72, 73, 74, 77, 102, 103, 104, 105, 106, 107, 109, 115, 116, 118, 124, 125, 127, 134, 135, 136, 138, 144, 145, 147, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 167, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 207, 220, 221, 225, 232, 233, 235, 242, 243, 245, 252, 253, 255, 262, 263, 265, 272, 273, 275, 282, 283, 287, 294, 295, 297, 304, 305, 307, 314, 315, 319, 325, 326, 328, 334, 335, 339, 353, 354, 356, 370, 371, 373, 386, 387, 389, 402, 403, 405, 425, 426, 430, 453, 454, 455, 456, 460, 469, 470, 471, 475, 482, 483, 484, 485, 487, 497, 498, 499, 500, 502, 518, 519, 521, 548, 549, 551, 574, 575, 579, 602, 603, 604, 606, 607, 611, 623, 624, 625, 626, 627, 628, 629, 630, 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645, 646, 650, 659, 660, 667, 713, 714, 718, 719, 720, 722, 734, 735, 737, 738, 739, 741, 745, 775, 776, 780, 781, 782], "summary": {"covered_lines": 195, "num_statements": 195, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"ExpandOption.__init__": {"executed_lines": [103, 104, 105, 106, 107], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 102}, "ExpandOption.select": {"executed_lines": [115, 116], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 109}, "ExpandOption.filter": {"executed_lines": [124, 125], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 118}, "ExpandOption.order_by": {"executed_lines": [134, 135, 136], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 127}, "ExpandOption.top": {"executed_lines": [144, 145], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 138}, "ExpandOption.to_odata": {"executed_lines": [153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 147}, "QueryBuilder.__init__": {"executed_lines": [191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 190}, "QueryBuilder.select": {"executed_lines": [220, 221], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 207}, "QueryBuilder.filter_eq": {"executed_lines": [232, 233], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 225}, "QueryBuilder.filter_ne": {"executed_lines": [242, 243], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 235}, "QueryBuilder.filter_gt": {"executed_lines": [252, 253], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 245}, "QueryBuilder.filter_ge": {"executed_lines": [262, 263], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 255}, "QueryBuilder.filter_lt": {"executed_lines": [272, 273], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 265}, "QueryBuilder.filter_le": {"executed_lines": [282, 283], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 275}, "QueryBuilder.filter_contains": {"executed_lines": [294, 295], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 287}, "QueryBuilder.filter_startswith": {"executed_lines": [304, 305], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 297}, "QueryBuilder.filter_endswith": {"executed_lines": [314, 315], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 307}, "QueryBuilder.filter_null": {"executed_lines": [325, 326], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 319}, "QueryBuilder.filter_not_null": {"executed_lines": [334, 335], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 328}, "QueryBuilder.filter_in": {"executed_lines": [353, 354], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 339}, "QueryBuilder.filter_not_in": {"executed_lines": [370, 371], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 356}, "QueryBuilder.filter_between": {"executed_lines": [386, 387], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 373}, "QueryBuilder.filter_not_between": {"executed_lines": [402, 403], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 389}, "QueryBuilder.filter_raw": {"executed_lines": [425, 426], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 405}, "QueryBuilder.where": {"executed_lines": [453, 454, 455, 456], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 430}, "QueryBuilder.order_by": {"executed_lines": [469, 470, 471], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 460}, "QueryBuilder.top": {"executed_lines": [482, 483, 484, 485], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 475}, "QueryBuilder.page_size": {"executed_lines": [497, 498, 499, 500], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 487}, "QueryBuilder.count": {"executed_lines": [518, 519], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 502}, "QueryBuilder.include_formatted_values": {"executed_lines": [548, 549], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 521}, "QueryBuilder.include_annotations": {"executed_lines": [574, 575], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 551}, "QueryBuilder.expand": {"executed_lines": [602, 603, 604, 606, 607], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 579}, "QueryBuilder.build": {"executed_lines": [623, 624, 625, 626, 627, 628, 629, 630, 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645, 646], "summary": {"covered_lines": 23, "num_statements": 23, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 611}, "QueryBuilder._validate_constraints": {"executed_lines": [659, 660], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 650}, "QueryBuilder.execute": {"executed_lines": [713, 714, 718, 719, 720, 722, 734, 735, 737, 741], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 667}, "QueryBuilder.execute._flat": {"executed_lines": [738, 739], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 737}, "QueryBuilder.to_dataframe": {"executed_lines": [775, 776, 780, 781, 782], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 745}, "": {"executed_lines": [47, 49, 51, 53, 54, 56, 59, 66, 67, 68, 69, 70, 71, 72, 73, 74, 77, 102, 109, 118, 127, 138, 147, 167, 190, 207, 225, 235, 245, 255, 265, 275, 287, 297, 307, 319, 328, 339, 356, 373, 389, 405, 430, 460, 475, 487, 502, 521, 551, 579, 611, 650, 667, 745], "summary": {"covered_lines": 54, "num_statements": 54, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"QueryParams": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 59}, "ExpandOption": {"executed_lines": [103, 104, 105, 106, 107, 115, 116, 124, 125, 134, 135, 136, 144, 145, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164], "summary": {"covered_lines": 26, "num_statements": 26, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 77}, "QueryBuilder": {"executed_lines": [191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 220, 221, 232, 233, 242, 243, 252, 253, 262, 263, 272, 273, 282, 283, 294, 295, 304, 305, 314, 315, 325, 326, 334, 335, 353, 354, 370, 371, 386, 387, 402, 403, 425, 426, 453, 454, 455, 456, 469, 470, 471, 482, 483, 484, 485, 497, 498, 499, 500, 518, 519, 548, 549, 574, 575, 602, 603, 604, 606, 607, 623, 624, 625, 626, 627, 628, 629, 630, 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645, 646, 659, 660, 713, 714, 718, 719, 720, 722, 734, 735, 737, 738, 739, 741, 775, 776, 780, 781, 782], "summary": {"covered_lines": 115, "num_statements": 115, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 167}, "": {"executed_lines": [47, 49, 51, 53, 54, 56, 59, 66, 67, 68, 69, 70, 71, 72, 73, 74, 77, 102, 109, 118, 127, 138, 147, 167, 190, 207, 225, 235, 245, 255, 265, 275, 287, 297, 307, 319, 328, 339, 356, 373, 389, 405, 430, 460, 475, 487, 502, 521, 551, 579, 611, 650, 667, 745], "summary": {"covered_lines": 54, "num_statements": 54, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\models\\record.py": {"executed_lines": [6, 8, 9, 11, 13, 16, 17, 41, 42, 43, 44, 48, 49, 51, 52, 54, 55, 57, 58, 60, 61, 63, 64, 66, 68, 70, 72, 74, 76, 78, 80, 84, 85, 106, 107, 108, 112, 114], "summary": {"covered_lines": 38, "num_statements": 38, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"Record.__getitem__": {"executed_lines": [49], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 48}, "Record.__setitem__": {"executed_lines": [52], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 51}, "Record.__delitem__": {"executed_lines": [55], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 54}, "Record.__contains__": {"executed_lines": [58], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 57}, "Record.__iter__": {"executed_lines": [61], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 60}, "Record.__len__": {"executed_lines": [64], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 63}, "Record.get": {"executed_lines": [68], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 66}, "Record.keys": {"executed_lines": [72], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 70}, "Record.values": {"executed_lines": [76], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 74}, "Record.items": {"executed_lines": [80], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 78}, "Record.from_api_response": {"executed_lines": [106, 107, 108], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 85}, "Record.to_dict": {"executed_lines": [114], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 112}, "": {"executed_lines": [6, 8, 9, 11, 13, 16, 17, 41, 42, 43, 44, 48, 51, 54, 57, 60, 63, 66, 70, 74, 78, 84, 85, 112], "summary": {"covered_lines": 24, "num_statements": 24, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"Record": {"executed_lines": [49, 52, 55, 58, 61, 64, 68, 72, 76, 80, 106, 107, 108, 114], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 17}, "": {"executed_lines": [6, 8, 9, 11, 13, 16, 17, 41, 42, 43, 44, 48, 51, 54, 57, 60, 63, 66, 70, 74, 78, 84, 85, 112], "summary": {"covered_lines": 24, "num_statements": 24, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\models\\relationship.py": {"executed_lines": [6, 8, 9, 11, 20, 23, 24, 52, 53, 54, 55, 56, 57, 58, 60, 77, 85, 86, 87, 90, 91, 116, 117, 118, 119, 120, 122, 142, 154, 155, 156, 157, 158, 161, 162, 185, 186, 187, 188, 189, 190, 191, 193, 215, 223, 224, 225, 226, 227, 230, 231, 251, 252, 253, 254, 255, 257, 278, 279, 286, 287, 288, 291, 292, 324, 325, 326, 329, 330, 331, 334, 335, 337, 338, 361, 370, 371, 391, 399, 400, 414, 415, 416, 418, 419, 429, 430, 437, 440], "summary": {"covered_lines": 89, "num_statements": 89, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"CascadeConfiguration.to_dict": {"executed_lines": [77, 85, 86, 87], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 60}, "LookupAttributeMetadata.to_dict": {"executed_lines": [142, 154, 155, 156, 157, 158], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 122}, "OneToManyRelationshipMetadata.to_dict": {"executed_lines": [215, 223, 224, 225, 226, 227], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 193}, "ManyToManyRelationshipMetadata.to_dict": {"executed_lines": [278, 279, 286, 287, 288], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 257}, "RelationshipInfo.from_one_to_many": {"executed_lines": [361], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 338}, "RelationshipInfo.from_many_to_many": {"executed_lines": [391], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 371}, "RelationshipInfo.from_api_response": {"executed_lines": [414, 415, 416, 418, 419, 429, 430, 437], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 400}, "": {"executed_lines": [6, 8, 9, 11, 20, 23, 24, 52, 53, 54, 55, 56, 57, 58, 60, 90, 91, 116, 117, 118, 119, 120, 122, 161, 162, 185, 186, 187, 188, 189, 190, 191, 193, 230, 231, 251, 252, 253, 254, 255, 257, 291, 292, 324, 325, 326, 329, 330, 331, 334, 335, 337, 338, 370, 371, 399, 400, 440], "summary": {"covered_lines": 58, "num_statements": 58, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"CascadeConfiguration": {"executed_lines": [77, 85, 86, 87], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 24}, "LookupAttributeMetadata": {"executed_lines": [142, 154, 155, 156, 157, 158], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 91}, "OneToManyRelationshipMetadata": {"executed_lines": [215, 223, 224, 225, 226, 227], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 162}, "ManyToManyRelationshipMetadata": {"executed_lines": [278, 279, 286, 287, 288], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 231}, "RelationshipInfo": {"executed_lines": [361, 391, 414, 415, 416, 418, 419, 429, 430, 437], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 292}, "": {"executed_lines": [6, 8, 9, 11, 20, 23, 24, 52, 53, 54, 55, 56, 57, 58, 60, 90, 91, 116, 117, 118, 119, 120, 122, 161, 162, 185, 186, 187, 188, 189, 190, 191, 193, 230, 231, 251, 252, 253, 254, 255, 257, 291, 292, 324, 325, 326, 329, 330, 331, 334, 335, 337, 338, 370, 371, 399, 400, 440], "summary": {"covered_lines": 58, "num_statements": 58, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\models\\table_info.py": {"executed_lines": [6, 8, 9, 11, 14, 15, 36, 37, 38, 39, 40, 41, 42, 43, 45, 46, 54, 55, 56, 59, 60, 61, 64, 65, 67, 79, 80, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 123, 135, 137, 139, 140, 141, 142, 143, 145, 146, 147, 148, 149, 151, 152, 154, 155, 157, 159, 160, 161, 162, 164, 166, 168, 170, 172, 174, 178, 179, 189, 199, 200, 208, 209, 210, 213, 214, 215, 217, 230, 232, 235, 236, 249, 250, 251, 252, 254, 255, 262], "summary": {"covered_lines": 88, "num_statements": 88, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"ColumnInfo.from_api_response": {"executed_lines": [54, 55, 56, 59, 60, 61, 64, 65, 67], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 46}, "TableInfo._resolve_key": {"executed_lines": [137], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 135}, "TableInfo.__getitem__": {"executed_lines": [140, 141, 142, 143], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 139}, "TableInfo.__contains__": {"executed_lines": [146, 147, 148, 149], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 145}, "TableInfo.__iter__": {"executed_lines": [152], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 151}, "TableInfo.__len__": {"executed_lines": [155], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 154}, "TableInfo.get": {"executed_lines": [159, 160, 161, 162], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 157}, "TableInfo.keys": {"executed_lines": [166], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 164}, "TableInfo.values": {"executed_lines": [170], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 168}, "TableInfo.items": {"executed_lines": [174], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 172}, "TableInfo.from_dict": {"executed_lines": [189], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 179}, "TableInfo.from_api_response": {"executed_lines": [208, 209, 210, 213, 214, 215, 217], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 200}, "TableInfo.to_dict": {"executed_lines": [232], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 230}, "AlternateKeyInfo.from_api_response": {"executed_lines": [262], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 255}, "": {"executed_lines": [6, 8, 9, 11, 14, 15, 36, 37, 38, 39, 40, 41, 42, 43, 45, 46, 79, 80, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 123, 135, 139, 145, 151, 154, 157, 164, 168, 172, 178, 179, 199, 200, 230, 235, 236, 249, 250, 251, 252, 254, 255], "summary": {"covered_lines": 51, "num_statements": 51, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"ColumnInfo": {"executed_lines": [54, 55, 56, 59, 60, 61, 64, 65, 67], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 15}, "TableInfo": {"executed_lines": [137, 140, 141, 142, 143, 146, 147, 148, 149, 152, 155, 159, 160, 161, 162, 166, 170, 174, 189, 208, 209, 210, 213, 214, 215, 217, 232], "summary": {"covered_lines": 27, "num_statements": 27, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 80}, "AlternateKeyInfo": {"executed_lines": [262], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 236}, "": {"executed_lines": [6, 8, 9, 11, 14, 15, 36, 37, 38, 39, 40, 41, 42, 43, 45, 46, 79, 80, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 123, 135, 139, 145, 151, 154, 157, 164, 168, 172, 178, 179, 199, 200, 230, 235, 236, 249, 250, 251, 252, 254, 255], "summary": {"covered_lines": 51, "num_statements": 51, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\models\\upsert.py": {"executed_lines": [6, 8, 9, 11, 14, 15, 38, 39], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [6, 8, 9, 11, 14, 15, 38, 39], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"UpsertItem": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 15}, "": {"executed_lines": [6, 8, 9, 11, 14, 15, 38, 39], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\operations\\__init__.py": {"executed_lines": [11, 13], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [11, 13], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [11, 13], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\operations\\batch.py": {"executed_lines": [6, 8, 10, 12, 13, 14, 35, 36, 37, 42, 47, 64, 75, 76, 78, 101, 103, 116, 118, 128, 131, 149, 150, 151, 153, 154, 156, 157, 165, 177, 178, 180, 197, 199, 219, 221, 244, 246, 274, 276, 316, 317, 318, 319, 320, 321, 322, 323, 325, 326, 329, 351, 352, 354, 383, 393, 402, 404, 413, 415, 436, 438, 450, 452, 465, 467, 484, 486, 500, 502, 509, 511, 520, 522, 558, 578, 589, 590, 592, 610, 611, 612, 620, 643, 644, 646, 665, 666, 667, 668, 670, 672, 673, 674, 675, 679, 681, 716, 717, 718, 719, 720, 721, 723, 724, 725, 726, 730, 732, 733, 738, 740, 741, 742, 743, 744, 745, 747, 749, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782, 790, 820, 821, 822, 823, 824, 825, 826, 827, 829, 844, 845, 846, 848, 866, 867, 870, 886, 887, 889, 895], "summary": {"covered_lines": 150, "num_statements": 151, "percent_covered": 99.33774834437087, "percent_covered_display": "99", "missing_lines": 1, "excluded_lines": 2, "percent_statements_covered": 99.33774834437087, "percent_statements_covered_display": "99"}, "missing_lines": [734], "excluded_lines": [44, 45], "functions": {"ChangeSetRecordOperations.__init__": {"executed_lines": [76], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 75}, "ChangeSetRecordOperations.create": {"executed_lines": [101], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 78}, "ChangeSetRecordOperations.update": {"executed_lines": [116], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 103}, "ChangeSetRecordOperations.delete": {"executed_lines": [128], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 118}, "ChangeSet.__init__": {"executed_lines": [150, 151], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 149}, "ChangeSet.__enter__": {"executed_lines": [154], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 153}, "ChangeSet.__exit__": {"executed_lines": [157], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 156}, "BatchRecordOperations.__init__": {"executed_lines": [178], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 177}, "BatchRecordOperations.create": {"executed_lines": [197], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 180}, "BatchRecordOperations.update": {"executed_lines": [219], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 199}, "BatchRecordOperations.delete": {"executed_lines": [244], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 221}, "BatchRecordOperations.get": {"executed_lines": [274], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 246}, "BatchRecordOperations.upsert": {"executed_lines": [316, 317, 318, 319, 320, 321, 322, 323, 325, 326], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 276}, "BatchTableOperations.__init__": {"executed_lines": [352], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 351}, "BatchTableOperations.create": {"executed_lines": [383], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 354}, "BatchTableOperations.delete": {"executed_lines": [402], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 393}, "BatchTableOperations.get": {"executed_lines": [413], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 404}, "BatchTableOperations.list": {"executed_lines": [436], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 415}, "BatchTableOperations.add_columns": {"executed_lines": [450], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 438}, "BatchTableOperations.remove_columns": {"executed_lines": [465], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 452}, "BatchTableOperations.create_one_to_many_relationship": {"executed_lines": [484], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 467}, "BatchTableOperations.create_many_to_many_relationship": {"executed_lines": [500], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 486}, "BatchTableOperations.delete_relationship": {"executed_lines": [509], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 502}, "BatchTableOperations.get_relationship": {"executed_lines": [520], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 511}, "BatchTableOperations.create_lookup_field": {"executed_lines": [558], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 522}, "BatchQueryOperations.__init__": {"executed_lines": [590], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 589}, "BatchQueryOperations.sql": {"executed_lines": [610, 611, 612], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 592}, "BatchDataFrameOperations.__init__": {"executed_lines": [644], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 643}, "BatchDataFrameOperations.create": {"executed_lines": [665, 666, 667, 668, 670, 672, 673, 674, 675, 679], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 646}, "BatchDataFrameOperations.update": {"executed_lines": [716, 717, 718, 719, 720, 721, 723, 724, 725, 726, 730, 732, 733, 738, 740, 741, 742, 743, 744, 745, 747], "summary": {"covered_lines": 21, "num_statements": 22, "percent_covered": 95.45454545454545, "percent_covered_display": "95", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 95.45454545454545, "percent_statements_covered_display": "95"}, "missing_lines": [734], "excluded_lines": [], "start_line": 681}, "BatchDataFrameOperations.delete": {"executed_lines": [773, 774, 775, 776, 777, 778, 779, 780, 781, 782], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 749}, "BatchRequest.__init__": {"executed_lines": [821, 822, 823, 824, 825, 826, 827], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 820}, "BatchRequest.changeset": {"executed_lines": [844, 845, 846], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 829}, "BatchRequest.execute": {"executed_lines": [866, 867], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 848}, "BatchOperations.__init__": {"executed_lines": [887], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 886}, "BatchOperations.new": {"executed_lines": [895], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 889}, "": {"executed_lines": [6, 8, 10, 12, 13, 14, 35, 36, 37, 42, 47, 64, 75, 78, 103, 118, 131, 149, 153, 156, 165, 177, 180, 199, 221, 246, 276, 329, 351, 354, 393, 404, 415, 438, 452, 467, 486, 502, 511, 522, 578, 589, 592, 620, 643, 646, 681, 749, 790, 820, 829, 848, 870, 886, 889], "summary": {"covered_lines": 55, "num_statements": 55, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [44, 45], "start_line": 1}}, "classes": {"ChangeSetRecordOperations": {"executed_lines": [76, 101, 116, 128], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 64}, "ChangeSet": {"executed_lines": [150, 151, 154, 157], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 131}, "BatchRecordOperations": {"executed_lines": [178, 197, 219, 244, 274, 316, 317, 318, 319, 320, 321, 322, 323, 325, 326], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 165}, "BatchTableOperations": {"executed_lines": [352, 383, 402, 413, 436, 450, 465, 484, 500, 509, 520, 558], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 329}, "BatchQueryOperations": {"executed_lines": [590, 610, 611, 612], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 578}, "BatchDataFrameOperations": {"executed_lines": [644, 665, 666, 667, 668, 670, 672, 673, 674, 675, 679, 716, 717, 718, 719, 720, 721, 723, 724, 725, 726, 730, 732, 733, 738, 740, 741, 742, 743, 744, 745, 747, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782], "summary": {"covered_lines": 42, "num_statements": 43, "percent_covered": 97.67441860465117, "percent_covered_display": "98", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 97.67441860465117, "percent_statements_covered_display": "98"}, "missing_lines": [734], "excluded_lines": [], "start_line": 620}, "BatchRequest": {"executed_lines": [821, 822, 823, 824, 825, 826, 827, 844, 845, 846, 866, 867], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 790}, "BatchOperations": {"executed_lines": [887, 895], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 870}, "": {"executed_lines": [6, 8, 10, 12, 13, 14, 35, 36, 37, 42, 47, 64, 75, 78, 103, 118, 131, 149, 153, 156, 165, 177, 180, 199, 221, 246, 276, 329, 351, 354, 393, 404, 415, 438, 452, 467, 486, 502, 511, 522, 578, 589, 592, 620, 643, 646, 681, 749, 790, 820, 829, 848, 870, 886, 889], "summary": {"covered_lines": 55, "num_statements": 55, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [44, 45], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\operations\\dataframe.py": {"executed_lines": [6, 8, 10, 12, 18, 21, 51, 52, 56, 91, 92, 93, 94, 98, 167, 168, 169, 170, 171, 172, 176, 181, 183, 184, 195, 197, 198, 199, 203, 238, 239, 241, 242, 244, 247, 248, 249, 254, 256, 257, 259, 263, 324, 325, 326, 327, 328, 329, 331, 332, 333, 334, 338, 340, 341, 342, 345, 348, 349, 350, 351, 352, 354, 355, 357, 361, 394, 395, 397, 398, 399, 401, 402, 403, 406, 408, 409, 410, 411], "summary": {"covered_lines": 79, "num_statements": 79, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [14, 15], "functions": {"DataFrameOperations.__init__": {"executed_lines": [52], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 51}, "DataFrameOperations.sql": {"executed_lines": [91, 92, 93, 94], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 56}, "DataFrameOperations.get": {"executed_lines": [167, 168, 169, 170, 171, 172, 176, 181, 183, 184, 195, 197, 198, 199], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 98}, "DataFrameOperations.create": {"executed_lines": [238, 239, 241, 242, 244, 247, 248, 249, 254, 256, 257, 259], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 203}, "DataFrameOperations.update": {"executed_lines": [324, 325, 326, 327, 328, 329, 331, 332, 333, 334, 338, 340, 341, 342, 345, 348, 349, 350, 351, 352, 354, 355, 357], "summary": {"covered_lines": 23, "num_statements": 23, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 263}, "DataFrameOperations.delete": {"executed_lines": [394, 395, 397, 398, 399, 401, 402, 403, 406, 408, 409, 410, 411], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 361}, "": {"executed_lines": [6, 8, 10, 12, 18, 21, 51, 56, 98, 203, 263, 361], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [14, 15], "start_line": 1}}, "classes": {"DataFrameOperations": {"executed_lines": [52, 91, 92, 93, 94, 167, 168, 169, 170, 171, 172, 176, 181, 183, 184, 195, 197, 198, 199, 238, 239, 241, 242, 244, 247, 248, 249, 254, 256, 257, 259, 324, 325, 326, 327, 328, 329, 331, 332, 333, 334, 338, 340, 341, 342, 345, 348, 349, 350, 351, 352, 354, 355, 357, 394, 395, 397, 398, 399, 401, 402, 403, 406, 408, 409, 410, 411], "summary": {"covered_lines": 67, "num_statements": 67, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 21}, "": {"executed_lines": [6, 8, 10, 12, 18, 21, 51, 56, 98, 203, 263, 361], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [14, 15], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\operations\\files.py": {"executed_lines": [6, 8, 14, 17, 35, 36, 40, 104, 105], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [10, 11], "functions": {"FileOperations.__init__": {"executed_lines": [36], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 35}, "FileOperations.upload": {"executed_lines": [104, 105], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 40}, "": {"executed_lines": [6, 8, 14, 17, 35, 40], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [10, 11], "start_line": 1}}, "classes": {"FileOperations": {"executed_lines": [36, 104, 105], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 17}, "": {"executed_lines": [6, 8, 14, 17, 35, 40], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [10, 11], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\operations\\query.py": {"executed_lines": [6, 8, 10, 11, 12, 18, 21, 49, 50, 54, 89, 90, 91, 95, 145, 146, 147, 151, 181, 190, 203, 204, 205, 206, 208, 209, 212, 213, 215, 216, 217, 218, 219, 220, 221, 230, 231, 235, 260, 261, 265, 309, 310, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 330, 331, 332, 333, 334, 335, 336, 337, 339, 349, 350, 354, 391, 392, 393, 394, 395, 400, 401, 402, 403, 411, 436, 437, 441, 478, 479, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 494, 495, 496, 497, 498, 499, 501, 511, 512, 516, 548, 549, 550, 551, 552, 557, 561, 597, 598, 599, 600, 601, 606, 607, 608, 609], "summary": {"covered_lines": 117, "num_statements": 120, "percent_covered": 97.5, "percent_covered_display": "98", "missing_lines": 3, "excluded_lines": 3, "percent_statements_covered": 97.5, "percent_statements_covered_display": "98"}, "missing_lines": [207, 323, 491], "excluded_lines": [14, 15, 445], "functions": {"QueryOperations.__init__": {"executed_lines": [50], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 49}, "QueryOperations.builder": {"executed_lines": [89, 90, 91], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 54}, "QueryOperations.sql": {"executed_lines": [145, 146, 147], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 95}, "QueryOperations.sql_columns": {"executed_lines": [181, 190, 203, 204, 205, 206, 208, 209, 212, 213, 215, 216, 217, 218, 219, 220, 221, 230, 231], "summary": {"covered_lines": 19, "num_statements": 20, "percent_covered": 95.0, "percent_covered_display": "95", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 95.0, "percent_statements_covered_display": "95"}, "missing_lines": [207], "excluded_lines": [], "start_line": 151}, "QueryOperations.sql_select": {"executed_lines": [260, 261], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 235}, "QueryOperations.sql_joins": {"executed_lines": [309, 310, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 330, 331, 332, 333, 334, 335, 336, 337, 339, 349, 350], "summary": {"covered_lines": 24, "num_statements": 25, "percent_covered": 96.0, "percent_covered_display": "96", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 96.0, "percent_statements_covered_display": "96"}, "missing_lines": [323], "excluded_lines": [], "start_line": 265}, "QueryOperations.sql_join": {"executed_lines": [391, 392, 393, 394, 395, 400, 401, 402, 403], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 354}, "QueryOperations.odata_select": {"executed_lines": [436, 437], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 411}, "QueryOperations.odata_expands": {"executed_lines": [478, 479, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 494, 495, 496, 497, 498, 499, 501, 511, 512], "summary": {"covered_lines": 21, "num_statements": 22, "percent_covered": 95.45454545454545, "percent_covered_display": "95", "missing_lines": 1, "excluded_lines": 1, "percent_statements_covered": 95.45454545454545, "percent_statements_covered_display": "95"}, "missing_lines": [491], "excluded_lines": [445], "start_line": 441}, "QueryOperations.odata_expand": {"executed_lines": [548, 549, 550, 551, 552, 557], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 516}, "QueryOperations.odata_bind": {"executed_lines": [597, 598, 599, 600, 601, 606, 607, 608, 609], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 561}, "": {"executed_lines": [6, 8, 10, 11, 12, 18, 21, 49, 54, 95, 151, 235, 265, 354, 411, 441, 516, 561], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [14, 15], "start_line": 1}}, "classes": {"QueryOperations": {"executed_lines": [50, 89, 90, 91, 145, 146, 147, 181, 190, 203, 204, 205, 206, 208, 209, 212, 213, 215, 216, 217, 218, 219, 220, 221, 230, 231, 260, 261, 309, 310, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 330, 331, 332, 333, 334, 335, 336, 337, 339, 349, 350, 391, 392, 393, 394, 395, 400, 401, 402, 403, 436, 437, 478, 479, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 494, 495, 496, 497, 498, 499, 501, 511, 512, 548, 549, 550, 551, 552, 557, 597, 598, 599, 600, 601, 606, 607, 608, 609], "summary": {"covered_lines": 99, "num_statements": 102, "percent_covered": 97.05882352941177, "percent_covered_display": "97", "missing_lines": 3, "excluded_lines": 1, "percent_statements_covered": 97.05882352941177, "percent_statements_covered_display": "97"}, "missing_lines": [207, 323, 491], "excluded_lines": [445], "start_line": 21}, "": {"executed_lines": [6, 8, 10, 11, 12, 18, 21, 49, 54, 95, 151, 235, 265, 354, 411, 441, 516, 561], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [14, 15], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\operations\\records.py": {"executed_lines": [6, 8, 10, 11, 17, 20, 46, 47, 57, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 110, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 174, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 228, 229, 262, 263, 331, 426, 427, 428, 429, 438, 443, 444, 445, 447, 448, 449, 460, 462, 466, 542, 543, 544, 545, 546, 547, 548, 549, 551, 552, 553, 554, 555, 556, 558, 559, 560, 561], "summary": {"covered_lines": 86, "num_statements": 86, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 18, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [13, 14, 51, 52, 53, 54, 55, 56, 168, 169, 170, 171, 172, 173, 260, 261, 329, 330], "functions": {"RecordOperations.__init__": {"executed_lines": [47], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 46}, "RecordOperations.create": {"executed_lines": [94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 57}, "RecordOperations.update": {"executed_lines": [155, 156, 157, 158, 159, 160, 161, 162, 163, 164], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 110}, "RecordOperations.delete": {"executed_lines": [210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 174}, "RecordOperations.get": {"executed_lines": [426, 427, 428, 429, 438, 443, 444, 445, 447, 462], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 331}, "RecordOperations.get._paged": {"executed_lines": [448, 449, 460], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 447}, "RecordOperations.upsert": {"executed_lines": [542, 543, 544, 545, 546, 547, 548, 549, 551, 552, 553, 554, 555, 556, 558, 559, 560, 561], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 466}, "": {"executed_lines": [6, 8, 10, 11, 17, 20, 46, 57, 110, 174, 228, 229, 262, 263, 331, 466], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 12, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [13, 14, 51, 53, 54, 56, 168, 170, 171, 173, 261, 330], "start_line": 1}}, "classes": {"RecordOperations": {"executed_lines": [47, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 426, 427, 428, 429, 438, 443, 444, 445, 447, 448, 449, 460, 462, 542, 543, 544, 545, 546, 547, 548, 549, 551, 552, 553, 554, 555, 556, 558, 559, 560, 561], "summary": {"covered_lines": 70, "num_statements": 70, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 6, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [52, 55, 169, 172, 260, 329], "start_line": 20}, "": {"executed_lines": [6, 8, 10, 11, 17, 20, 46, 57, 110, 174, 228, 229, 262, 263, 331, 466], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 12, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [13, 14, 51, 53, 54, 56, 168, 170, 171, 173, 261, 330], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\operations\\tables.py": {"executed_lines": [6, 8, 10, 17, 18, 19, 20, 26, 29, 65, 66, 70, 135, 136, 143, 147, 164, 165, 169, 189, 190, 191, 192, 193, 197, 245, 246, 250, 278, 279, 283, 311, 312, 316, 380, 381, 386, 396, 436, 437, 441, 450, 469, 470, 474, 493, 494, 495, 496, 497, 501, 567, 568, 579, 583, 634, 635, 636, 637, 646, 668, 669, 670, 674, 698, 699, 703, 749, 750, 754, 793, 794, 798, 838, 839], "summary": {"covered_lines": 75, "num_statements": 75, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [22, 23], "functions": {"TableOperations.__init__": {"executed_lines": [66], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 65}, "TableOperations.create": {"executed_lines": [135, 136, 143], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 70}, "TableOperations.delete": {"executed_lines": [164, 165], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 147}, "TableOperations.get": {"executed_lines": [189, 190, 191, 192, 193], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 169}, "TableOperations.list": {"executed_lines": [245, 246], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 197}, "TableOperations.add_columns": {"executed_lines": [278, 279], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 250}, "TableOperations.remove_columns": {"executed_lines": [311, 312], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 283}, "TableOperations.create_one_to_many_relationship": {"executed_lines": [380, 381, 386], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 316}, "TableOperations.create_many_to_many_relationship": {"executed_lines": [436, 437, 441], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 396}, "TableOperations.delete_relationship": {"executed_lines": [469, 470], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 450}, "TableOperations.get_relationship": {"executed_lines": [493, 494, 495, 496, 497], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 474}, "TableOperations.create_lookup_field": {"executed_lines": [567, 568, 579], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 501}, "TableOperations.create_alternate_key": {"executed_lines": [634, 635, 636, 637], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 583}, "TableOperations.get_alternate_keys": {"executed_lines": [668, 669, 670], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 646}, "TableOperations.delete_alternate_key": {"executed_lines": [698, 699], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 674}, "TableOperations.list_columns": {"executed_lines": [749, 750], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 703}, "TableOperations.list_relationships": {"executed_lines": [793, 794], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 754}, "TableOperations.list_table_relationships": {"executed_lines": [838, 839], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 798}, "": {"executed_lines": [6, 8, 10, 17, 18, 19, 20, 26, 29, 65, 70, 147, 169, 197, 250, 283, 316, 396, 450, 474, 501, 583, 646, 674, 703, 754, 798], "summary": {"covered_lines": 27, "num_statements": 27, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [22, 23], "start_line": 1}}, "classes": {"TableOperations": {"executed_lines": [66, 135, 136, 143, 164, 165, 189, 190, 191, 192, 193, 245, 246, 278, 279, 311, 312, 380, 381, 386, 436, 437, 441, 469, 470, 493, 494, 495, 496, 497, 567, 568, 579, 634, 635, 636, 637, 668, 669, 670, 698, 699, 749, 750, 793, 794, 838, 839], "summary": {"covered_lines": 48, "num_statements": 48, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 29}, "": {"executed_lines": [6, 8, 10, 17, 18, 19, 20, 26, 29, 65, 70, 147, 169, 197, 250, 283, 316, 396, 450, 474, 501, 583, 646, 674, 703, 754, 798], "summary": {"covered_lines": 27, "num_statements": 27, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [22, 23], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\utils\\__init__.py": {"executed_lines": [10], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [10], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [10], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\utils\\_pandas.py": {"executed_lines": [6, 8, 9, 11, 12, 15, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 36, 43, 44, 45, 46, 47, 48, 49, 50, 51, 55, 56, 58, 59, 60], "summary": {"covered_lines": 34, "num_statements": 34, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"_normalize_scalar": {"executed_lines": [21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 15}, "dataframe_to_records": {"executed_lines": [43, 44, 45, 46, 47, 48, 49, 50, 51, 55, 56, 58, 59, 60], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 36}, "": {"executed_lines": [6, 8, 9, 11, 12, 15, 36], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [6, 8, 9, 11, 12, 15, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 36, 43, 44, 45, 46, 47, 48, 49, 50, 51, 55, 56, 58, 59, 60], "summary": {"covered_lines": 34, "num_statements": 34, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\__init__.py": {"executed_lines": [4], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [4], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [4], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}}, "totals": {"covered_lines": 4692, "num_statements": 4956, "percent_covered": 94.67312348668281, "percent_covered_display": "95", "missing_lines": 264, "excluded_lines": 71, "percent_statements_covered": 94.67312348668281, "percent_statements_covered_display": "95"}} \ No newline at end of file diff --git a/coverage.xml b/coverage.xml new file mode 100644 index 00000000..e3dcc7ce --- /dev/null +++ b/coverage.xml @@ -0,0 +1,3181 @@ + + + + + + C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/coverage_aio.json b/coverage_aio.json new file mode 100644 index 00000000..b75e36fa --- /dev/null +++ b/coverage_aio.json @@ -0,0 +1 @@ +{"meta": {"format": 3, "version": "7.13.5", "timestamp": "2026-04-27T21:26:52.956879", "branch_coverage": false, "show_contexts": false}, "files": {"src\\PowerPlatform\\Dataverse\\__init__.py": {"executed_lines": [4, 6, 8], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [4, 6, 8], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [4, 6, 8], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\_skill_installer.py": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 139, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 139, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [13, 14, 15, 18, 24, 28, 29, 31, 34, 35, 36, 40, 41, 42, 44, 47, 49, 50, 56, 66, 67, 70, 71, 73, 74, 77, 78, 79, 80, 81, 83, 84, 85, 86, 87, 88, 91, 92, 93, 94, 95, 96, 97, 98, 101, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 129, 132, 139, 141, 142, 144, 145, 146, 147, 149, 150, 151, 152, 153, 154, 155, 156, 158, 159, 160, 162, 163, 164, 167, 169, 171, 172, 174, 175, 176, 177, 179, 180, 181, 182, 183, 184, 185, 187, 188, 190, 191, 193, 194, 195, 196, 197, 198, 199, 201, 202, 205, 207, 209, 238, 242, 244, 246, 248, 249, 250, 251, 254, 255, 256, 258, 259, 260, 263, 264, 267, 268], "excluded_lines": [], "functions": {"get_skill_source_paths": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 11, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 11, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [24, 28, 29, 31, 34, 35, 36, 40, 41, 42, 44], "excluded_lines": [], "start_line": 18}, "get_skill_destination_paths": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [49, 50], "excluded_lines": [], "start_line": 47}, "install_skill": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 49, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 49, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [66, 67, 70, 71, 73, 74, 77, 78, 79, 80, 81, 83, 84, 85, 86, 87, 88, 91, 92, 93, 94, 95, 96, 97, 98, 101, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 129], "excluded_lines": [], "start_line": 56}, "uninstall_skill": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 21, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 21, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [139, 141, 142, 144, 145, 146, 147, 149, 150, 151, 152, 153, 154, 155, 156, 158, 159, 160, 162, 163, 164], "excluded_lines": [], "start_line": 132}, "check_skill_status": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 27, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 27, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [169, 171, 172, 174, 175, 176, 177, 179, 180, 181, 182, 183, 184, 185, 187, 188, 190, 191, 193, 194, 195, 196, 197, 198, 199, 201, 202], "excluded_lines": [], "start_line": 167}, "main": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 18, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 18, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [207, 209, 238, 242, 244, 246, 248, 249, 250, 251, 254, 255, 256, 258, 259, 260, 263, 264], "excluded_lines": [], "start_line": 205}, "": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 11, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 11, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [13, 14, 15, 18, 47, 56, 132, 167, 205, 267, 268], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 139, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 139, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [13, 14, 15, 18, 24, 28, 29, 31, 34, 35, 36, 40, 41, 42, 44, 47, 49, 50, 56, 66, 67, 70, 71, 73, 74, 77, 78, 79, 80, 81, 83, 84, 85, 86, 87, 88, 91, 92, 93, 94, 95, 96, 97, 98, 101, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 129, 132, 139, 141, 142, 144, 145, 146, 147, 149, 150, 151, 152, 153, 154, 155, 156, 158, 159, 160, 162, 163, 164, 167, 169, 171, 172, 174, 175, 176, 177, 179, 180, 181, 182, 183, 184, 185, 187, 188, 190, 191, 193, 194, 195, 196, 197, 198, 199, 201, 202, 205, 207, 209, 238, 242, 244, 246, 248, 249, 250, 251, 254, 255, 256, 258, 259, 260, 263, 264, 267, 268], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\__init__.py": {"executed_lines": [4, 6], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [4, 6], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [4, 6], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\async_client.py": {"executed_lines": [4, 6, 7, 9, 10, 12, 13, 14, 15, 16, 17, 18, 19, 20, 23, 93, 99, 100, 101, 102, 103, 104, 105, 106, 109, 110, 111, 112, 113, 114, 116, 135, 136, 146, 158, 159, 160, 161, 163, 169, 171, 188, 189, 190, 191, 192, 193, 194, 195, 196, 198, 200, 201], "summary": {"covered_lines": 53, "num_statements": 60, "percent_covered": 88.33333333333333, "percent_covered_display": "88", "missing_lines": 7, "excluded_lines": 0, "percent_statements_covered": 88.33333333333333, "percent_statements_covered_display": "88"}, "missing_lines": [126, 127, 133, 138, 139, 141, 142], "excluded_lines": [], "functions": {"AsyncDataverseClient.__init__": {"executed_lines": [99, 100, 101, 102, 103, 104, 105, 106, 109, 110, 111, 112, 113, 114], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 93}, "AsyncDataverseClient._get_odata": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 3, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [126, 127, 133], "excluded_lines": [], "start_line": 116}, "AsyncDataverseClient._scoped_odata": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 4, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [138, 139, 141, 142], "excluded_lines": [], "start_line": 136}, "AsyncDataverseClient.__aenter__": {"executed_lines": [158, 159, 160, 161], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 146}, "AsyncDataverseClient.__aexit__": {"executed_lines": [169], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 163}, "AsyncDataverseClient.aclose": {"executed_lines": [188, 189, 190, 191, 192, 193, 194, 195, 196], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 171}, "AsyncDataverseClient._check_closed": {"executed_lines": [200, 201], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 198}, "": {"executed_lines": [4, 6, 7, 9, 10, 12, 13, 14, 15, 16, 17, 18, 19, 20, 23, 93, 116, 135, 136, 146, 163, 171, 198], "summary": {"covered_lines": 23, "num_statements": 23, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"AsyncDataverseClient": {"executed_lines": [99, 100, 101, 102, 103, 104, 105, 106, 109, 110, 111, 112, 113, 114, 158, 159, 160, 161, 169, 188, 189, 190, 191, 192, 193, 194, 195, 196, 200, 201], "summary": {"covered_lines": 30, "num_statements": 37, "percent_covered": 81.08108108108108, "percent_covered_display": "81", "missing_lines": 7, "excluded_lines": 0, "percent_statements_covered": 81.08108108108108, "percent_statements_covered_display": "81"}, "missing_lines": [126, 127, 133, 138, 139, 141, 142], "excluded_lines": [], "start_line": 23}, "": {"executed_lines": [4, 6, 7, 9, 10, 12, 13, 14, 15, 16, 17, 18, 19, 20, 23, 93, 116, 135, 136, 146, 163, 171, 198], "summary": {"covered_lines": 23, "num_statements": 23, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\core\\__init__.py": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\core\\_async_auth.py": {"executed_lines": [13, 15, 17, 20, 29, 30, 31, 32, 34, 44, 45], "summary": {"covered_lines": 11, "num_statements": 11, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"_AsyncAuthManager.__init__": {"executed_lines": [30, 31, 32], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 29}, "_AsyncAuthManager._acquire_token": {"executed_lines": [44, 45], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 34}, "": {"executed_lines": [13, 15, 17, 20, 29, 34], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_AsyncAuthManager": {"executed_lines": [30, 31, 32, 44, 45], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 20}, "": {"executed_lines": [13, 15, 17, 20, 29, 34], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\core\\_async_http.py": {"executed_lines": [12, 14, 15, 16, 18, 24, 47, 55, 56, 57, 58, 59, 61, 82, 83, 87, 88, 89, 91, 92, 93, 97, 98, 100, 103, 104, 112, 113, 114, 115, 118, 119, 121, 124, 125, 133, 134, 135, 136, 143, 144, 145, 146, 147, 149, 154, 155, 156], "summary": {"covered_lines": 48, "num_statements": 50, "percent_covered": 96.0, "percent_covered_display": "96", "missing_lines": 2, "excluded_lines": 2, "percent_statements_covered": 96.0, "percent_statements_covered_display": "96"}, "missing_lines": [99, 101], "excluded_lines": [20, 21], "functions": {"_AsyncHttpClient.__init__": {"executed_lines": [55, 56, 57, 58, 59], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 47}, "_AsyncHttpClient._request": {"executed_lines": [82, 83, 87, 88, 89, 91, 92, 93, 97, 98, 100, 103, 104, 112, 113, 114, 115, 118, 119, 121, 124, 125, 133, 134, 135, 136, 143, 144, 145, 146, 147], "summary": {"covered_lines": 31, "num_statements": 33, "percent_covered": 93.93939393939394, "percent_covered_display": "94", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 93.93939393939394, "percent_statements_covered_display": "94"}, "missing_lines": [99, 101], "excluded_lines": [], "start_line": 61}, "_AsyncHttpClient.close": {"executed_lines": [154, 155, 156], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 149}, "": {"executed_lines": [12, 14, 15, 16, 18, 24, 47, 61, 149], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [20, 21], "start_line": 1}}, "classes": {"_AsyncHttpClient": {"executed_lines": [55, 56, 57, 58, 59, 82, 83, 87, 88, 89, 91, 92, 93, 97, 98, 100, 103, 104, 112, 113, 114, 115, 118, 119, 121, 124, 125, 133, 134, 135, 136, 143, 144, 145, 146, 147, 154, 155, 156], "summary": {"covered_lines": 39, "num_statements": 41, "percent_covered": 95.1219512195122, "percent_covered_display": "95", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 95.1219512195122, "percent_statements_covered_display": "95"}, "missing_lines": [99, 101], "excluded_lines": [], "start_line": 24}, "": {"executed_lines": [12, 14, 15, 16, 18, 24, 47, 61, 149], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [20, 21], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\data\\__init__.py": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\data\\_async_batch.py": {"executed_lines": [6, 8, 9, 11, 12, 13, 14, 15, 42, 45, 61, 67, 76, 90, 158, 173, 214, 228, 234, 242, 255, 258, 273, 283, 287, 291, 311], "summary": {"covered_lines": 27, "num_statements": 149, "percent_covered": 18.120805369127517, "percent_covered_display": "18", "missing_lines": 122, "excluded_lines": 2, "percent_statements_covered": 18.120805369127517, "percent_statements_covered_display": "18"}, "missing_lines": [62, 63, 64, 65, 68, 101, 102, 104, 106, 107, 108, 115, 116, 118, 121, 122, 124, 125, 140, 141, 142, 143, 144, 146, 152, 159, 160, 161, 162, 164, 165, 166, 167, 168, 170, 171, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 216, 217, 218, 222, 229, 230, 231, 232, 235, 236, 237, 238, 239, 240, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 256, 259, 260, 261, 262, 263, 264, 265, 275, 276, 277, 281, 284, 285, 288, 289, 292, 293, 294, 295, 296, 299, 300, 304, 305, 312], "excluded_lines": [39, 40], "functions": {"_SyncResponseWrapper.__init__": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 4, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [62, 63, 64, 65], "excluded_lines": [], "start_line": 61}, "_SyncResponseWrapper.json": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [68], "excluded_lines": [], "start_line": 67}, "_AsyncBatchClient.execute": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 20, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 20, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [101, 102, 104, 106, 107, 108, 115, 116, 118, 121, 122, 124, 125, 140, 141, 142, 143, 144, 146, 152], "excluded_lines": [], "start_line": 90}, "_AsyncBatchClient._resolve_all": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 11, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 11, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [159, 160, 161, 162, 164, 165, 166, 167, 168, 170, 171], "excluded_lines": [], "start_line": 158}, "_AsyncBatchClient._resolve_item": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 35, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 35, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209], "excluded_lines": [], "start_line": 173}, "_AsyncBatchClient._resolve_one": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 4, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [216, 217, 218, 222], "excluded_lines": [], "start_line": 214}, "_AsyncBatchClient._resolve_record_create": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 4, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [229, 230, 231, 232], "excluded_lines": [], "start_line": 228}, "_AsyncBatchClient._resolve_record_update": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 6, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 6, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [235, 236, 237, 238, 239, 240], "excluded_lines": [], "start_line": 234}, "_AsyncBatchClient._resolve_record_delete": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 11, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 11, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253], "excluded_lines": [], "start_line": 242}, "_AsyncBatchClient._resolve_record_get": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [256], "excluded_lines": [], "start_line": 255}, "_AsyncBatchClient._resolve_record_upsert": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 7, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 7, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [259, 260, 261, 262, 263, 264, 265], "excluded_lines": [], "start_line": 258}, "_AsyncBatchClient._require_entity_metadata": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 4, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [275, 276, 277, 281], "excluded_lines": [], "start_line": 273}, "_AsyncBatchClient._resolve_table_delete": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [284, 285], "excluded_lines": [], "start_line": 283}, "_AsyncBatchClient._resolve_table_add_columns": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [288, 289], "excluded_lines": [], "start_line": 287}, "_AsyncBatchClient._resolve_table_remove_columns": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 9, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 9, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [292, 293, 294, 295, 296, 299, 300, 304, 305], "excluded_lines": [], "start_line": 291}, "_AsyncBatchClient._resolve_query_sql": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [312], "excluded_lines": [], "start_line": 311}, "": {"executed_lines": [6, 8, 9, 11, 12, 13, 14, 15, 42, 45, 61, 67, 76, 90, 158, 173, 214, 228, 234, 242, 255, 258, 273, 283, 287, 291, 311], "summary": {"covered_lines": 27, "num_statements": 27, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [39, 40], "start_line": 1}}, "classes": {"_SyncResponseWrapper": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 5, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 5, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [62, 63, 64, 65, 68], "excluded_lines": [], "start_line": 45}, "_AsyncBatchClient": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 117, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 117, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [101, 102, 104, 106, 107, 108, 115, 116, 118, 121, 122, 124, 125, 140, 141, 142, 143, 144, 146, 152, 159, 160, 161, 162, 164, 165, 166, 167, 168, 170, 171, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 216, 217, 218, 222, 229, 230, 231, 232, 235, 236, 237, 238, 239, 240, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 256, 259, 260, 261, 262, 263, 264, 265, 275, 276, 277, 281, 284, 285, 288, 289, 292, 293, 294, 295, 296, 299, 300, 304, 305, 312], "excluded_lines": [], "start_line": 76}, "": {"executed_lines": [6, 8, 9, 11, 12, 13, 14, 15, 42, 45, 61, 67, 76, 90, 158, 173, 214, 228, 234, 242, 255, 258, 273, 283, 287, 291, 311], "summary": {"covered_lines": 27, "num_statements": 27, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [39, 40], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\data\\_async_odata.py": {"executed_lines": [6, 8, 10, 11, 12, 13, 14, 15, 17, 19, 21, 22, 23, 24, 25, 26, 39, 49, 52, 83, 93, 106, 109, 199, 218, 250, 299, 331, 388, 403, 443, 474, 488, 516, 529, 550, 642, 759, 810, 833, 869, 905, 949, 983, 998, 1052, 1096, 1118, 1148, 1170, 1221, 1247, 1274, 1358, 1414, 1488, 1506, 1529, 1557, 1583, 1605, 1627, 1666, 1689, 1731, 1745], "summary": {"covered_lines": 66, "num_statements": 699, "percent_covered": 9.44206008583691, "percent_covered_display": "9", "missing_lines": 633, "excluded_lines": 0, "percent_statements_covered": 9.44206008583691, "percent_statements_covered_display": "9"}, "missing_lines": [73, 74, 75, 89, 90, 91, 95, 96, 97, 107, 120, 122, 123, 124, 125, 126, 127, 129, 137, 138, 139, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 160, 161, 162, 163, 164, 165, 166, 167, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 210, 211, 212, 213, 214, 215, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 271, 272, 273, 274, 275, 276, 277, 278, 279, 281, 282, 283, 285, 286, 288, 289, 290, 292, 293, 294, 295, 296, 297, 325, 326, 327, 328, 329, 362, 363, 367, 368, 369, 370, 371, 372, 373, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 390, 391, 392, 393, 395, 396, 397, 398, 399, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 458, 459, 460, 461, 465, 466, 467, 468, 469, 470, 471, 472, 486, 511, 512, 513, 514, 527, 547, 548, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 597, 598, 599, 600, 601, 602, 603, 605, 606, 607, 608, 610, 611, 613, 614, 616, 617, 619, 620, 621, 622, 623, 625, 626, 627, 628, 630, 631, 632, 634, 635, 636, 637, 638, 639, 661, 662, 663, 664, 665, 670, 672, 673, 674, 675, 676, 679, 680, 681, 682, 683, 685, 686, 687, 690, 691, 692, 693, 694, 696, 697, 706, 707, 711, 712, 713, 714, 723, 724, 725, 726, 727, 728, 735, 736, 737, 738, 739, 746, 747, 748, 749, 750, 751, 752, 753, 754, 756, 764, 765, 768, 769, 770, 771, 772, 774, 775, 776, 780, 781, 782, 783, 784, 785, 786, 787, 792, 796, 797, 798, 799, 803, 804, 805, 806, 807, 821, 823, 824, 825, 829, 830, 831, 840, 841, 853, 854, 855, 856, 857, 861, 862, 865, 866, 867, 876, 877, 878, 879, 880, 881, 882, 883, 884, 885, 886, 887, 888, 889, 893, 894, 895, 896, 897, 898, 899, 900, 901, 902, 903, 933, 934, 935, 939, 940, 941, 942, 943, 944, 945, 946, 947, 962, 963, 964, 965, 967, 968, 969, 970, 971, 972, 973, 974, 975, 978, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 1006, 1007, 1008, 1009, 1010, 1012, 1013, 1018, 1019, 1020, 1022, 1023, 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1034, 1035, 1036, 1037, 1038, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1050, 1061, 1064, 1068, 1069, 1072, 1075, 1076, 1077, 1078, 1079, 1081, 1082, 1083, 1084, 1085, 1086, 1087, 1088, 1089, 1090, 1091, 1092, 1093, 1094, 1105, 1106, 1107, 1108, 1145, 1146, 1160, 1161, 1162, 1166, 1197, 1198, 1199, 1204, 1205, 1206, 1210, 1211, 1212, 1213, 1215, 1235, 1236, 1237, 1242, 1243, 1244, 1245, 1263, 1264, 1265, 1270, 1271, 1272, 1304, 1305, 1306, 1311, 1315, 1316, 1318, 1322, 1323, 1324, 1325, 1326, 1327, 1328, 1329, 1331, 1332, 1333, 1334, 1335, 1337, 1338, 1339, 1341, 1348, 1378, 1379, 1381, 1382, 1383, 1388, 1389, 1390, 1392, 1393, 1394, 1395, 1399, 1400, 1401, 1406, 1407, 1409, 1410, 1412, 1435, 1436, 1437, 1438, 1440, 1442, 1443, 1444, 1446, 1447, 1448, 1454, 1455, 1456, 1457, 1459, 1460, 1461, 1462, 1467, 1468, 1469, 1471, 1473, 1474, 1475, 1476, 1477, 1479, 1481, 1482, 1484, 1497, 1498, 1499, 1513, 1514, 1515, 1516, 1517, 1518, 1519, 1520, 1521, 1522, 1523, 1542, 1543, 1544, 1545, 1547, 1548, 1549, 1569, 1570, 1571, 1572, 1573, 1574, 1575, 1576, 1577, 1591, 1592, 1593, 1594, 1595, 1596, 1600, 1602, 1603, 1617, 1618, 1619, 1620, 1621, 1635, 1636, 1641, 1642, 1643, 1644, 1645, 1646, 1647, 1650, 1651, 1655, 1656, 1657, 1658, 1659, 1660, 1677, 1678, 1680, 1681, 1682, 1691, 1692, 1693, 1694, 1725, 1739, 1740, 1741, 1742, 1743, 1760, 1761, 1762], "excluded_lines": [], "functions": {"_AsyncODataClient.__init__": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 3, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [73, 74, 75], "excluded_lines": [], "start_line": 52}, "_AsyncODataClient.close": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 3, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [89, 90, 91], "excluded_lines": [], "start_line": 83}, "_AsyncODataClient._headers": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 3, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [95, 96, 97], "excluded_lines": [], "start_line": 93}, "_AsyncODataClient._raw_request": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [107], "excluded_lines": [], "start_line": 106}, "_AsyncODataClient._request": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 42, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 42, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [120, 122, 129, 137, 138, 139, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 160, 161, 162, 163, 164, 165, 166, 167, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181], "excluded_lines": [], "start_line": 109}, "_AsyncODataClient._request._merge": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 5, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 5, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [123, 124, 125, 126, 127], "excluded_lines": [], "start_line": 122}, "_AsyncODataClient._execute_raw": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 6, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 6, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [210, 211, 212, 213, 214, 215], "excluded_lines": [], "start_line": 199}, "_AsyncODataClient._create": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 13, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 13, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246], "excluded_lines": [], "start_line": 218}, "_AsyncODataClient._create_multiple": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 23, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 23, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [271, 272, 273, 274, 275, 276, 277, 278, 279, 281, 282, 283, 285, 286, 288, 289, 290, 292, 293, 294, 295, 296, 297], "excluded_lines": [], "start_line": 250}, "_AsyncODataClient._upsert": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 5, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 5, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [325, 326, 327, 328, 329], "excluded_lines": [], "start_line": 299}, "_AsyncODataClient._upsert_multiple": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 19, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 19, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [362, 363, 367, 368, 369, 370, 371, 372, 373, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385], "excluded_lines": [], "start_line": 331}, "_AsyncODataClient._primary_id_attr": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 9, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 9, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [390, 391, 392, 393, 395, 396, 397, 398, 399], "excluded_lines": [], "start_line": 388}, "_AsyncODataClient._update_by_ids": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 21, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 21, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441], "excluded_lines": [], "start_line": 403}, "_AsyncODataClient._delete_multiple": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 12, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 12, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [458, 459, 460, 461, 465, 466, 467, 468, 469, 470, 471, 472], "excluded_lines": [], "start_line": 443}, "_AsyncODataClient._update": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [486], "excluded_lines": [], "start_line": 474}, "_AsyncODataClient._update_multiple": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 4, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [511, 512, 513, 514], "excluded_lines": [], "start_line": 488}, "_AsyncODataClient._delete": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [527], "excluded_lines": [], "start_line": 516}, "_AsyncODataClient._get": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [547, 548], "excluded_lines": [], "start_line": 529}, "_AsyncODataClient._get_multiple": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 39, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 39, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 597, 605, 606, 607, 608, 610, 611, 613, 614, 616, 617, 619, 620, 621, 622, 623, 625, 626, 627, 628, 630, 631, 632, 634, 635, 636, 637, 638, 639], "excluded_lines": [], "start_line": 550}, "_AsyncODataClient._get_multiple._do_request": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 6, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 6, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [598, 599, 600, 601, 602, 603], "excluded_lines": [], "start_line": 597}, "_AsyncODataClient._query_sql": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 53, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 53, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [661, 662, 663, 664, 665, 670, 672, 673, 674, 675, 676, 679, 680, 681, 682, 683, 685, 686, 687, 690, 691, 692, 693, 694, 696, 697, 706, 707, 711, 712, 713, 714, 723, 724, 725, 726, 727, 728, 735, 736, 737, 738, 739, 746, 747, 748, 749, 750, 751, 752, 753, 754, 756], "excluded_lines": [], "start_line": 642}, "_AsyncODataClient._entity_set_from_schema_name": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 28, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 28, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [764, 765, 768, 769, 770, 771, 772, 774, 775, 776, 780, 781, 782, 783, 784, 785, 786, 787, 792, 796, 797, 798, 799, 803, 804, 805, 806, 807], "excluded_lines": [], "start_line": 759}, "_AsyncODataClient._get_entity_by_table_schema_name": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 7, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 7, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [821, 823, 824, 825, 829, 830, 831], "excluded_lines": [], "start_line": 810}, "_AsyncODataClient._create_entity": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 12, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 12, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [840, 841, 853, 854, 855, 856, 857, 861, 862, 865, 866, 867], "excluded_lines": [], "start_line": 833}, "_AsyncODataClient._get_attribute_metadata": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 25, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 25, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [876, 877, 878, 879, 880, 881, 882, 883, 884, 885, 886, 887, 888, 889, 893, 894, 895, 896, 897, 898, 899, 900, 901, 902, 903], "excluded_lines": [], "start_line": 869}, "_AsyncODataClient._list_columns": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 12, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 12, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [933, 934, 935, 939, 940, 941, 942, 943, 944, 945, 946, 947], "excluded_lines": [], "start_line": 905}, "_AsyncODataClient._wait_for_attribute_visibility": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 14, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 14, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [962, 963, 964, 965, 967, 968, 969, 970, 971, 972, 973, 974, 975, 978], "excluded_lines": [], "start_line": 949}, "_AsyncODataClient._request_metadata_with_retry": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 12, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 12, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996], "excluded_lines": [], "start_line": 983}, "_AsyncODataClient._bulk_fetch_picklists": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 38, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 38, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1006, 1007, 1008, 1009, 1010, 1012, 1013, 1018, 1019, 1020, 1022, 1023, 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1034, 1035, 1036, 1037, 1038, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1050], "excluded_lines": [], "start_line": 998}, "_AsyncODataClient._convert_labels_to_ints": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 24, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 24, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1061, 1064, 1068, 1069, 1072, 1075, 1076, 1077, 1078, 1079, 1081, 1082, 1083, 1084, 1085, 1086, 1087, 1088, 1089, 1090, 1091, 1092, 1093, 1094], "excluded_lines": [], "start_line": 1052}, "_AsyncODataClient._get_table_info": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 4, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1105, 1106, 1107, 1108], "excluded_lines": [], "start_line": 1096}, "_AsyncODataClient._list_tables": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1145, 1146], "excluded_lines": [], "start_line": 1118}, "_AsyncODataClient._delete_table": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 4, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1160, 1161, 1162, 1166], "excluded_lines": [], "start_line": 1148}, "_AsyncODataClient._create_alternate_key": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 11, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 11, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1197, 1198, 1199, 1204, 1205, 1206, 1210, 1211, 1212, 1213, 1215], "excluded_lines": [], "start_line": 1170}, "_AsyncODataClient._get_alternate_keys": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 7, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 7, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1235, 1236, 1237, 1242, 1243, 1244, 1245], "excluded_lines": [], "start_line": 1221}, "_AsyncODataClient._delete_alternate_key": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 6, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 6, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1263, 1264, 1265, 1270, 1271, 1272], "excluded_lines": [], "start_line": 1247}, "_AsyncODataClient._create_table": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 25, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 25, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1304, 1305, 1306, 1311, 1315, 1316, 1318, 1322, 1323, 1324, 1325, 1326, 1327, 1328, 1329, 1331, 1332, 1333, 1334, 1335, 1337, 1338, 1339, 1341, 1348], "excluded_lines": [], "start_line": 1274}, "_AsyncODataClient._create_columns": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 20, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 20, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1378, 1379, 1381, 1382, 1383, 1388, 1389, 1390, 1392, 1393, 1394, 1395, 1399, 1400, 1401, 1406, 1407, 1409, 1410, 1412], "excluded_lines": [], "start_line": 1358}, "_AsyncODataClient._delete_columns": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 32, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 32, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1435, 1436, 1437, 1438, 1440, 1442, 1443, 1444, 1446, 1447, 1448, 1454, 1455, 1456, 1457, 1459, 1460, 1461, 1462, 1467, 1468, 1469, 1471, 1473, 1474, 1475, 1476, 1477, 1479, 1481, 1482, 1484], "excluded_lines": [], "start_line": 1414}, "_AsyncODataClient._build_create": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 3, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1497, 1498, 1499], "excluded_lines": [], "start_line": 1488}, "_AsyncODataClient._build_create_multiple": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 11, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 11, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1513, 1514, 1515, 1516, 1517, 1518, 1519, 1520, 1521, 1522, 1523], "excluded_lines": [], "start_line": 1506}, "_AsyncODataClient._build_update": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 7, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 7, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1542, 1543, 1544, 1545, 1547, 1548, 1549], "excluded_lines": [], "start_line": 1529}, "_AsyncODataClient._build_update_multiple_from_records": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 9, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 9, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1569, 1570, 1571, 1572, 1573, 1574, 1575, 1576, 1577], "excluded_lines": [], "start_line": 1557}, "_AsyncODataClient._build_update_multiple": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 9, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 9, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1591, 1592, 1593, 1594, 1595, 1596, 1600, 1602, 1603], "excluded_lines": [], "start_line": 1583}, "_AsyncODataClient._build_upsert": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 5, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 5, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1617, 1618, 1619, 1620, 1621], "excluded_lines": [], "start_line": 1605}, "_AsyncODataClient._build_upsert_multiple": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 17, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 17, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1635, 1636, 1641, 1642, 1643, 1644, 1645, 1646, 1647, 1650, 1651, 1655, 1656, 1657, 1658, 1659, 1660], "excluded_lines": [], "start_line": 1627}, "_AsyncODataClient._build_delete": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 5, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 5, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1677, 1678, 1680, 1681, 1682], "excluded_lines": [], "start_line": 1666}, "_AsyncODataClient._build_delete_multiple": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 5, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 5, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1691, 1692, 1693, 1694, 1725], "excluded_lines": [], "start_line": 1689}, "_AsyncODataClient._build_get": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 5, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 5, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1739, 1740, 1741, 1742, 1743], "excluded_lines": [], "start_line": 1731}, "_AsyncODataClient._build_sql": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 3, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1760, 1761, 1762], "excluded_lines": [], "start_line": 1745}, "": {"executed_lines": [6, 8, 10, 11, 12, 13, 14, 15, 17, 19, 21, 22, 23, 24, 25, 26, 39, 49, 52, 83, 93, 106, 109, 199, 218, 250, 299, 331, 388, 403, 443, 474, 488, 516, 529, 550, 642, 759, 810, 833, 869, 905, 949, 983, 998, 1052, 1096, 1118, 1148, 1170, 1221, 1247, 1274, 1358, 1414, 1488, 1506, 1529, 1557, 1583, 1605, 1627, 1666, 1689, 1731, 1745], "summary": {"covered_lines": 66, "num_statements": 66, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_AsyncODataClient": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 633, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 633, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [73, 74, 75, 89, 90, 91, 95, 96, 97, 107, 120, 122, 123, 124, 125, 126, 127, 129, 137, 138, 139, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 160, 161, 162, 163, 164, 165, 166, 167, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 210, 211, 212, 213, 214, 215, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 271, 272, 273, 274, 275, 276, 277, 278, 279, 281, 282, 283, 285, 286, 288, 289, 290, 292, 293, 294, 295, 296, 297, 325, 326, 327, 328, 329, 362, 363, 367, 368, 369, 370, 371, 372, 373, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 390, 391, 392, 393, 395, 396, 397, 398, 399, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 458, 459, 460, 461, 465, 466, 467, 468, 469, 470, 471, 472, 486, 511, 512, 513, 514, 527, 547, 548, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 597, 598, 599, 600, 601, 602, 603, 605, 606, 607, 608, 610, 611, 613, 614, 616, 617, 619, 620, 621, 622, 623, 625, 626, 627, 628, 630, 631, 632, 634, 635, 636, 637, 638, 639, 661, 662, 663, 664, 665, 670, 672, 673, 674, 675, 676, 679, 680, 681, 682, 683, 685, 686, 687, 690, 691, 692, 693, 694, 696, 697, 706, 707, 711, 712, 713, 714, 723, 724, 725, 726, 727, 728, 735, 736, 737, 738, 739, 746, 747, 748, 749, 750, 751, 752, 753, 754, 756, 764, 765, 768, 769, 770, 771, 772, 774, 775, 776, 780, 781, 782, 783, 784, 785, 786, 787, 792, 796, 797, 798, 799, 803, 804, 805, 806, 807, 821, 823, 824, 825, 829, 830, 831, 840, 841, 853, 854, 855, 856, 857, 861, 862, 865, 866, 867, 876, 877, 878, 879, 880, 881, 882, 883, 884, 885, 886, 887, 888, 889, 893, 894, 895, 896, 897, 898, 899, 900, 901, 902, 903, 933, 934, 935, 939, 940, 941, 942, 943, 944, 945, 946, 947, 962, 963, 964, 965, 967, 968, 969, 970, 971, 972, 973, 974, 975, 978, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 1006, 1007, 1008, 1009, 1010, 1012, 1013, 1018, 1019, 1020, 1022, 1023, 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1034, 1035, 1036, 1037, 1038, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1050, 1061, 1064, 1068, 1069, 1072, 1075, 1076, 1077, 1078, 1079, 1081, 1082, 1083, 1084, 1085, 1086, 1087, 1088, 1089, 1090, 1091, 1092, 1093, 1094, 1105, 1106, 1107, 1108, 1145, 1146, 1160, 1161, 1162, 1166, 1197, 1198, 1199, 1204, 1205, 1206, 1210, 1211, 1212, 1213, 1215, 1235, 1236, 1237, 1242, 1243, 1244, 1245, 1263, 1264, 1265, 1270, 1271, 1272, 1304, 1305, 1306, 1311, 1315, 1316, 1318, 1322, 1323, 1324, 1325, 1326, 1327, 1328, 1329, 1331, 1332, 1333, 1334, 1335, 1337, 1338, 1339, 1341, 1348, 1378, 1379, 1381, 1382, 1383, 1388, 1389, 1390, 1392, 1393, 1394, 1395, 1399, 1400, 1401, 1406, 1407, 1409, 1410, 1412, 1435, 1436, 1437, 1438, 1440, 1442, 1443, 1444, 1446, 1447, 1448, 1454, 1455, 1456, 1457, 1459, 1460, 1461, 1462, 1467, 1468, 1469, 1471, 1473, 1474, 1475, 1476, 1477, 1479, 1481, 1482, 1484, 1497, 1498, 1499, 1513, 1514, 1515, 1516, 1517, 1518, 1519, 1520, 1521, 1522, 1523, 1542, 1543, 1544, 1545, 1547, 1548, 1549, 1569, 1570, 1571, 1572, 1573, 1574, 1575, 1576, 1577, 1591, 1592, 1593, 1594, 1595, 1596, 1600, 1602, 1603, 1617, 1618, 1619, 1620, 1621, 1635, 1636, 1641, 1642, 1643, 1644, 1645, 1646, 1647, 1650, 1651, 1655, 1656, 1657, 1658, 1659, 1660, 1677, 1678, 1680, 1681, 1682, 1691, 1692, 1693, 1694, 1725, 1739, 1740, 1741, 1742, 1743, 1760, 1761, 1762], "excluded_lines": [], "start_line": 49}, "": {"executed_lines": [6, 8, 10, 11, 12, 13, 14, 15, 17, 19, 21, 22, 23, 24, 25, 26, 39, 49, 52, 83, 93, 106, 109, 199, 218, 250, 299, 331, 388, 403, 443, 474, 488, 516, 529, 550, 642, 759, 810, 833, 869, 905, 949, 983, 998, 1052, 1096, 1118, 1148, 1170, 1221, 1247, 1274, 1358, 1414, 1488, 1506, 1529, 1557, 1583, 1605, 1627, 1666, 1689, 1731, 1745], "summary": {"covered_lines": 66, "num_statements": 66, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\data\\_async_relationships.py": {"executed_lines": [10, 12, 14, 15, 18, 28, 74, 114, 128, 147, 180, 250], "summary": {"covered_lines": 12, "num_statements": 71, "percent_covered": 16.901408450704224, "percent_covered_display": "17", "missing_lines": 59, "excluded_lines": 0, "percent_statements_covered": 16.901408450704224, "percent_statements_covered_display": "17"}, "missing_lines": [51, 54, 55, 57, 58, 59, 61, 64, 66, 94, 96, 98, 99, 100, 102, 105, 107, 123, 124, 125, 126, 140, 141, 142, 143, 144, 145, 171, 172, 173, 174, 175, 176, 177, 178, 210, 211, 213, 214, 215, 220, 228, 229, 230, 231, 232, 233, 234, 236, 237, 238, 240, 241, 242, 244, 260, 261, 262, 263], "excluded_lines": [], "functions": {"_AsyncRelationshipOperationsMixin._create_one_to_many_relationship": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 9, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 9, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [51, 54, 55, 57, 58, 59, 61, 64, 66], "excluded_lines": [], "start_line": 28}, "_AsyncRelationshipOperationsMixin._create_many_to_many_relationship": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 8, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 8, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [94, 96, 98, 99, 100, 102, 105, 107], "excluded_lines": [], "start_line": 74}, "_AsyncRelationshipOperationsMixin._delete_relationship": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 4, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [123, 124, 125, 126], "excluded_lines": [], "start_line": 114}, "_AsyncRelationshipOperationsMixin._get_relationship": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 6, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 6, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [140, 141, 142, 143, 144, 145], "excluded_lines": [], "start_line": 128}, "_AsyncRelationshipOperationsMixin._list_relationships": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 8, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 8, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [171, 172, 173, 174, 175, 176, 177, 178], "excluded_lines": [], "start_line": 147}, "_AsyncRelationshipOperationsMixin._list_table_relationships": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 20, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 20, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [210, 211, 213, 214, 215, 220, 228, 229, 230, 231, 232, 233, 234, 236, 237, 238, 240, 241, 242, 244], "excluded_lines": [], "start_line": 180}, "_AsyncRelationshipOperationsMixin._extract_id_from_header": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 4, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [260, 261, 262, 263], "excluded_lines": [], "start_line": 250}, "": {"executed_lines": [10, 12, 14, 15, 18, 28, 74, 114, 128, 147, 180, 250], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_AsyncRelationshipOperationsMixin": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 59, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 59, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [51, 54, 55, 57, 58, 59, 61, 64, 66, 94, 96, 98, 99, 100, 102, 105, 107, 123, 124, 125, 126, 140, 141, 142, 143, 144, 145, 171, 172, 173, 174, 175, 176, 177, 178, 210, 211, 213, 214, 215, 220, 228, 229, 230, 231, 232, 233, 234, 236, 237, 238, 240, 241, 242, 244, 260, 261, 262, 263], "excluded_lines": [], "start_line": 18}, "": {"executed_lines": [10, 12, 14, 15, 18, 28, 74, 114, 128, 147, 180, 250], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\data\\_async_upload.py": {"executed_lines": [6, 8, 11, 14, 80, 117], "summary": {"covered_lines": 6, "num_statements": 87, "percent_covered": 6.896551724137931, "percent_covered_display": "7", "missing_lines": 81, "excluded_lines": 0, "percent_statements_covered": 6.896551724137931, "percent_statements_covered_display": "7"}, "missing_lines": [43, 46, 49, 50, 51, 52, 53, 54, 56, 59, 61, 63, 64, 65, 66, 67, 70, 72, 73, 76, 77, 78, 90, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 109, 110, 112, 114, 115, 147, 148, 150, 151, 152, 153, 154, 155, 156, 157, 158, 161, 162, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 193, 194, 195], "excluded_lines": [], "functions": {"_AsyncFileUploadMixin._upload_file": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 22, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 22, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [43, 46, 49, 50, 51, 52, 53, 54, 56, 59, 61, 63, 64, 65, 66, 67, 70, 72, 73, 76, 77, 78], "excluded_lines": [], "start_line": 14}, "_AsyncFileUploadMixin._upload_file_small": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 20, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 20, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [90, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 109, 110, 112, 114, 115], "excluded_lines": [], "start_line": 80}, "_AsyncFileUploadMixin._upload_file_chunk": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 39, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 39, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [147, 148, 150, 151, 152, 153, 154, 155, 156, 157, 158, 161, 162, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 193, 194, 195], "excluded_lines": [], "start_line": 117}, "": {"executed_lines": [6, 8, 11, 14, 80, 117], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_AsyncFileUploadMixin": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 81, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 81, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [43, 46, 49, 50, 51, 52, 53, 54, 56, 59, 61, 63, 64, 65, 66, 67, 70, 72, 73, 76, 77, 78, 90, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 109, 110, 112, 114, 115, 147, 148, 150, 151, 152, 153, 154, 155, 156, 157, 158, 161, 162, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 193, 194, 195], "excluded_lines": [], "start_line": 11}, "": {"executed_lines": [6, 8, 11, 14, 80, 117], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\operations\\__init__.py": {"executed_lines": [11, 13], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [11, 13], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [11, 13], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\operations\\async_batch.py": {"executed_lines": [6, 8, 10, 12, 13, 14, 34, 35, 36, 37, 42, 47, 64, 75, 76, 78, 101, 103, 116, 118, 128, 131, 149, 150, 151, 153, 154, 156, 157, 165, 177, 178, 180, 197, 199, 219, 221, 243, 245, 265, 267, 286, 287, 288, 289, 290, 291, 292, 293, 295, 296, 299, 315, 316, 318, 341, 351, 360, 362, 369, 371, 385, 387, 398, 400, 412, 414, 431, 433, 447, 449, 456, 458, 465, 467, 503, 523, 534, 535, 537, 554, 555, 556, 564, 574, 575, 577, 591, 592, 593, 594, 596, 598, 599, 600, 601, 605, 607, 629, 630, 631, 632, 633, 634, 636, 637, 638, 639, 643, 645, 646, 647, 651, 653, 654, 655, 656, 657, 658, 660, 662, 681, 682, 683, 684, 685, 686, 687, 688, 689, 690, 698, 727, 728, 729, 730, 731, 732, 733, 734, 736, 751, 752, 753, 755, 773, 774, 777, 793, 794, 796, 802], "summary": {"covered_lines": 152, "num_statements": 152, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [44, 45], "functions": {"AsyncChangeSetRecordOperations.__init__": {"executed_lines": [76], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 75}, "AsyncChangeSetRecordOperations.create": {"executed_lines": [101], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 78}, "AsyncChangeSetRecordOperations.update": {"executed_lines": [116], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 103}, "AsyncChangeSetRecordOperations.delete": {"executed_lines": [128], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 118}, "AsyncChangeSet.__init__": {"executed_lines": [150, 151], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 149}, "AsyncChangeSet.__aenter__": {"executed_lines": [154], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 153}, "AsyncChangeSet.__aexit__": {"executed_lines": [157], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 156}, "AsyncBatchRecordOperations.__init__": {"executed_lines": [178], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 177}, "AsyncBatchRecordOperations.create": {"executed_lines": [197], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 180}, "AsyncBatchRecordOperations.update": {"executed_lines": [219], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 199}, "AsyncBatchRecordOperations.delete": {"executed_lines": [243], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 221}, "AsyncBatchRecordOperations.get": {"executed_lines": [265], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 245}, "AsyncBatchRecordOperations.upsert": {"executed_lines": [286, 287, 288, 289, 290, 291, 292, 293, 295, 296], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 267}, "AsyncBatchTableOperations.__init__": {"executed_lines": [316], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 315}, "AsyncBatchTableOperations.create": {"executed_lines": [341], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 318}, "AsyncBatchTableOperations.delete": {"executed_lines": [360], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 351}, "AsyncBatchTableOperations.get": {"executed_lines": [369], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 362}, "AsyncBatchTableOperations.list": {"executed_lines": [385], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 371}, "AsyncBatchTableOperations.add_columns": {"executed_lines": [398], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 387}, "AsyncBatchTableOperations.remove_columns": {"executed_lines": [412], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 400}, "AsyncBatchTableOperations.create_one_to_many_relationship": {"executed_lines": [431], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 414}, "AsyncBatchTableOperations.create_many_to_many_relationship": {"executed_lines": [447], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 433}, "AsyncBatchTableOperations.delete_relationship": {"executed_lines": [456], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 449}, "AsyncBatchTableOperations.get_relationship": {"executed_lines": [465], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 458}, "AsyncBatchTableOperations.create_lookup_field": {"executed_lines": [503], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 467}, "AsyncBatchQueryOperations.__init__": {"executed_lines": [535], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 534}, "AsyncBatchQueryOperations.sql": {"executed_lines": [554, 555, 556], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 537}, "AsyncBatchDataFrameOperations.__init__": {"executed_lines": [575], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 574}, "AsyncBatchDataFrameOperations.create": {"executed_lines": [591, 592, 593, 594, 596, 598, 599, 600, 601, 605], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 577}, "AsyncBatchDataFrameOperations.update": {"executed_lines": [629, 630, 631, 632, 633, 634, 636, 637, 638, 639, 643, 645, 646, 647, 651, 653, 654, 655, 656, 657, 658, 660], "summary": {"covered_lines": 22, "num_statements": 22, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 607}, "AsyncBatchDataFrameOperations.delete": {"executed_lines": [681, 682, 683, 684, 685, 686, 687, 688, 689, 690], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 662}, "AsyncBatchRequest.__init__": {"executed_lines": [728, 729, 730, 731, 732, 733, 734], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 727}, "AsyncBatchRequest.changeset": {"executed_lines": [751, 752, 753], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 736}, "AsyncBatchRequest.execute": {"executed_lines": [773, 774], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 755}, "AsyncBatchOperations.__init__": {"executed_lines": [794], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 793}, "AsyncBatchOperations.new": {"executed_lines": [802], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 796}, "": {"executed_lines": [6, 8, 10, 12, 13, 14, 34, 35, 36, 37, 42, 47, 64, 75, 78, 103, 118, 131, 149, 153, 156, 165, 177, 180, 199, 221, 245, 267, 299, 315, 318, 351, 362, 371, 387, 400, 414, 433, 449, 458, 467, 523, 534, 537, 564, 574, 577, 607, 662, 698, 727, 736, 755, 777, 793, 796], "summary": {"covered_lines": 56, "num_statements": 56, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [44, 45], "start_line": 1}}, "classes": {"AsyncChangeSetRecordOperations": {"executed_lines": [76, 101, 116, 128], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 64}, "AsyncChangeSet": {"executed_lines": [150, 151, 154, 157], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 131}, "AsyncBatchRecordOperations": {"executed_lines": [178, 197, 219, 243, 265, 286, 287, 288, 289, 290, 291, 292, 293, 295, 296], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 165}, "AsyncBatchTableOperations": {"executed_lines": [316, 341, 360, 369, 385, 398, 412, 431, 447, 456, 465, 503], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 299}, "AsyncBatchQueryOperations": {"executed_lines": [535, 554, 555, 556], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 523}, "AsyncBatchDataFrameOperations": {"executed_lines": [575, 591, 592, 593, 594, 596, 598, 599, 600, 601, 605, 629, 630, 631, 632, 633, 634, 636, 637, 638, 639, 643, 645, 646, 647, 651, 653, 654, 655, 656, 657, 658, 660, 681, 682, 683, 684, 685, 686, 687, 688, 689, 690], "summary": {"covered_lines": 43, "num_statements": 43, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 564}, "AsyncBatchRequest": {"executed_lines": [728, 729, 730, 731, 732, 733, 734, 751, 752, 753, 773, 774], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 698}, "AsyncBatchOperations": {"executed_lines": [794, 802], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 777}, "": {"executed_lines": [6, 8, 10, 12, 13, 14, 34, 35, 36, 37, 42, 47, 64, 75, 78, 103, 118, 131, 149, 153, 156, 165, 177, 180, 199, 221, 245, 267, 299, 315, 318, 351, 362, 371, 387, 400, 414, 433, 449, 458, 467, 523, 534, 537, 564, 574, 577, 607, 662, 698, 727, 736, 755, 777, 793, 796], "summary": {"covered_lines": 56, "num_statements": 56, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [44, 45], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\operations\\async_dataframe.py": {"executed_lines": [6, 8, 10, 12, 18, 21, 51, 52, 56, 82, 83, 84, 85, 89, 151, 152, 153, 154, 155, 156, 160, 165, 167, 168, 179, 181, 182, 183, 187, 217, 218, 220, 221, 223, 226, 227, 228, 233, 235, 236, 238, 242, 280, 281, 282, 283, 284, 285, 287, 288, 289, 290, 294, 296, 297, 298, 301, 304, 305, 306, 307, 308, 310, 311, 313, 317, 350, 351, 353, 354, 355, 357, 358, 359, 363, 365, 366, 367, 368], "summary": {"covered_lines": 79, "num_statements": 79, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [14, 15], "functions": {"AsyncDataFrameOperations.__init__": {"executed_lines": [52], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 51}, "AsyncDataFrameOperations.sql": {"executed_lines": [82, 83, 84, 85], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 56}, "AsyncDataFrameOperations.get": {"executed_lines": [151, 152, 153, 154, 155, 156, 160, 165, 167, 168, 179, 181, 182, 183], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 89}, "AsyncDataFrameOperations.create": {"executed_lines": [217, 218, 220, 221, 223, 226, 227, 228, 233, 235, 236, 238], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 187}, "AsyncDataFrameOperations.update": {"executed_lines": [280, 281, 282, 283, 284, 285, 287, 288, 289, 290, 294, 296, 297, 298, 301, 304, 305, 306, 307, 308, 310, 311, 313], "summary": {"covered_lines": 23, "num_statements": 23, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 242}, "AsyncDataFrameOperations.delete": {"executed_lines": [350, 351, 353, 354, 355, 357, 358, 359, 363, 365, 366, 367, 368], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 317}, "": {"executed_lines": [6, 8, 10, 12, 18, 21, 51, 56, 89, 187, 242, 317], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [14, 15], "start_line": 1}}, "classes": {"AsyncDataFrameOperations": {"executed_lines": [52, 82, 83, 84, 85, 151, 152, 153, 154, 155, 156, 160, 165, 167, 168, 179, 181, 182, 183, 217, 218, 220, 221, 223, 226, 227, 228, 233, 235, 236, 238, 280, 281, 282, 283, 284, 285, 287, 288, 289, 290, 294, 296, 297, 298, 301, 304, 305, 306, 307, 308, 310, 311, 313, 350, 351, 353, 354, 355, 357, 358, 359, 363, 365, 366, 367, 368], "summary": {"covered_lines": 67, "num_statements": 67, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 21}, "": {"executed_lines": [6, 8, 10, 12, 18, 21, 51, 56, 89, 187, 242, 317], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [14, 15], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\operations\\async_files.py": {"executed_lines": [6, 8, 14, 17, 35, 36, 40, 104, 105], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [10, 11], "functions": {"AsyncFileOperations.__init__": {"executed_lines": [36], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 35}, "AsyncFileOperations.upload": {"executed_lines": [104, 105], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 40}, "": {"executed_lines": [6, 8, 14, 17, 35, 40], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [10, 11], "start_line": 1}}, "classes": {"AsyncFileOperations": {"executed_lines": [36, 104, 105], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 17}, "": {"executed_lines": [6, 8, 14, 17, 35, 40], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [10, 11], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\operations\\async_query.py": {"executed_lines": [6, 8, 10, 11, 17, 20, 39, 40, 44, 93, 94, 95, 99, 129, 138, 151, 152, 153, 154, 156, 157, 160, 161, 163, 164, 165, 166, 167, 169, 178, 179, 183, 208, 209, 213, 246, 247, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 267, 268, 269, 270, 273, 274, 276, 286, 287, 291, 327, 328, 329, 330, 331, 336, 337, 338, 339, 347, 372, 373, 377, 407, 408, 410, 411, 412, 413, 415, 416, 417, 418, 419, 423, 424, 425, 426, 427, 428, 430, 440, 441, 445, 477, 478, 479, 480, 481, 486, 490, 526, 527, 528, 529, 530, 535, 536, 537, 538], "summary": {"covered_lines": 108, "num_statements": 115, "percent_covered": 93.91304347826087, "percent_covered_display": "94", "missing_lines": 7, "excluded_lines": 2, "percent_statements_covered": 93.91304347826087, "percent_statements_covered_display": "94"}, "missing_lines": [155, 168, 260, 271, 272, 414, 420], "excluded_lines": [13, 14], "functions": {"AsyncQueryOperations.__init__": {"executed_lines": [40], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 39}, "AsyncQueryOperations.sql": {"executed_lines": [93, 94, 95], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 44}, "AsyncQueryOperations.sql_columns": {"executed_lines": [129, 138, 151, 152, 153, 154, 156, 157, 160, 161, 163, 164, 165, 166, 167, 169, 178, 179], "summary": {"covered_lines": 18, "num_statements": 20, "percent_covered": 90.0, "percent_covered_display": "90", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 90.0, "percent_statements_covered_display": "90"}, "missing_lines": [155, 168], "excluded_lines": [], "start_line": 99}, "AsyncQueryOperations.sql_select": {"executed_lines": [208, 209], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 183}, "AsyncQueryOperations.sql_joins": {"executed_lines": [246, 247, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 267, 268, 269, 270, 273, 274, 276, 286, 287], "summary": {"covered_lines": 22, "num_statements": 25, "percent_covered": 88.0, "percent_covered_display": "88", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 88.0, "percent_statements_covered_display": "88"}, "missing_lines": [260, 271, 272], "excluded_lines": [], "start_line": 213}, "AsyncQueryOperations.sql_join": {"executed_lines": [327, 328, 329, 330, 331, 336, 337, 338, 339], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 291}, "AsyncQueryOperations.odata_select": {"executed_lines": [372, 373], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 347}, "AsyncQueryOperations.odata_expands": {"executed_lines": [407, 408, 410, 411, 412, 413, 415, 416, 417, 418, 419, 423, 424, 425, 426, 427, 428, 430, 440, 441], "summary": {"covered_lines": 20, "num_statements": 22, "percent_covered": 90.9090909090909, "percent_covered_display": "91", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 90.9090909090909, "percent_statements_covered_display": "91"}, "missing_lines": [414, 420], "excluded_lines": [], "start_line": 377}, "AsyncQueryOperations.odata_expand": {"executed_lines": [477, 478, 479, 480, 481, 486], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 445}, "AsyncQueryOperations.odata_bind": {"executed_lines": [526, 527, 528, 529, 530, 535, 536, 537, 538], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 490}, "": {"executed_lines": [6, 8, 10, 11, 17, 20, 39, 44, 99, 183, 213, 291, 347, 377, 445, 490], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [13, 14], "start_line": 1}}, "classes": {"AsyncQueryOperations": {"executed_lines": [40, 93, 94, 95, 129, 138, 151, 152, 153, 154, 156, 157, 160, 161, 163, 164, 165, 166, 167, 169, 178, 179, 208, 209, 246, 247, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 267, 268, 269, 270, 273, 274, 276, 286, 287, 327, 328, 329, 330, 331, 336, 337, 338, 339, 372, 373, 407, 408, 410, 411, 412, 413, 415, 416, 417, 418, 419, 423, 424, 425, 426, 427, 428, 430, 440, 441, 477, 478, 479, 480, 481, 486, 526, 527, 528, 529, 530, 535, 536, 537, 538], "summary": {"covered_lines": 92, "num_statements": 99, "percent_covered": 92.92929292929293, "percent_covered_display": "93", "missing_lines": 7, "excluded_lines": 0, "percent_statements_covered": 92.92929292929293, "percent_statements_covered_display": "93"}, "missing_lines": [155, 168, 260, 271, 272, 414, 420], "excluded_lines": [], "start_line": 20}, "": {"executed_lines": [6, 8, 10, 11, 17, 20, 39, 44, 99, 183, 213, 291, 347, 377, 445, 490], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [13, 14], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\operations\\async_records.py": {"executed_lines": [6, 8, 10, 11, 17, 20, 46, 47, 57, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 110, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 174, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 228, 229, 262, 263, 331, 428, 429, 430, 431, 440, 445, 446, 447, 449, 450, 451, 462, 464, 468, 516, 517, 518, 519, 520, 521, 522, 523, 525, 526, 527, 528, 529, 530, 532, 533, 534, 535], "summary": {"covered_lines": 86, "num_statements": 86, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 18, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [13, 14, 51, 52, 53, 54, 55, 56, 168, 169, 170, 171, 172, 173, 260, 261, 329, 330], "functions": {"AsyncRecordOperations.__init__": {"executed_lines": [47], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 46}, "AsyncRecordOperations.create": {"executed_lines": [94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 57}, "AsyncRecordOperations.update": {"executed_lines": [155, 156, 157, 158, 159, 160, 161, 162, 163, 164], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 110}, "AsyncRecordOperations.delete": {"executed_lines": [210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 174}, "AsyncRecordOperations.get": {"executed_lines": [428, 429, 430, 431, 440, 445, 446, 447, 449, 464], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 331}, "AsyncRecordOperations.get._paged": {"executed_lines": [450, 451, 462], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 449}, "AsyncRecordOperations.upsert": {"executed_lines": [516, 517, 518, 519, 520, 521, 522, 523, 525, 526, 527, 528, 529, 530, 532, 533, 534, 535], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 468}, "": {"executed_lines": [6, 8, 10, 11, 17, 20, 46, 57, 110, 174, 228, 229, 262, 263, 331, 468], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 12, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [13, 14, 51, 53, 54, 56, 168, 170, 171, 173, 261, 330], "start_line": 1}}, "classes": {"AsyncRecordOperations": {"executed_lines": [47, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 428, 429, 430, 431, 440, 445, 446, 447, 449, 450, 451, 462, 464, 516, 517, 518, 519, 520, 521, 522, 523, 525, 526, 527, 528, 529, 530, 532, 533, 534, 535], "summary": {"covered_lines": 70, "num_statements": 70, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 6, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [52, 55, 169, 172, 260, 329], "start_line": 20}, "": {"executed_lines": [6, 8, 10, 11, 17, 20, 46, 57, 110, 174, 228, 229, 262, 263, 331, 468], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 12, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [13, 14, 51, 53, 54, 56, 168, 170, 171, 173, 261, 330], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\operations\\async_tables.py": {"executed_lines": [6, 8, 10, 16, 17, 18, 19, 25, 28, 64, 65, 69, 134, 135, 142, 146, 163, 164, 168, 188, 189, 190, 191, 192, 196, 244, 245, 249, 277, 278, 282, 310, 311, 315, 373, 374, 379, 389, 429, 430, 434, 443, 462, 463, 467, 486, 487, 488, 489, 490, 494, 559, 560, 571, 575, 626, 627, 628, 629, 638, 660, 661, 662, 666, 690, 691, 695, 735, 736, 740, 769, 770, 774, 814, 815], "summary": {"covered_lines": 75, "num_statements": 75, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [21, 22], "functions": {"AsyncTableOperations.__init__": {"executed_lines": [65], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 64}, "AsyncTableOperations.create": {"executed_lines": [134, 135, 142], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 69}, "AsyncTableOperations.delete": {"executed_lines": [163, 164], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 146}, "AsyncTableOperations.get": {"executed_lines": [188, 189, 190, 191, 192], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 168}, "AsyncTableOperations.list": {"executed_lines": [244, 245], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 196}, "AsyncTableOperations.add_columns": {"executed_lines": [277, 278], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 249}, "AsyncTableOperations.remove_columns": {"executed_lines": [310, 311], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 282}, "AsyncTableOperations.create_one_to_many_relationship": {"executed_lines": [373, 374, 379], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 315}, "AsyncTableOperations.create_many_to_many_relationship": {"executed_lines": [429, 430, 434], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 389}, "AsyncTableOperations.delete_relationship": {"executed_lines": [462, 463], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 443}, "AsyncTableOperations.get_relationship": {"executed_lines": [486, 487, 488, 489, 490], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 467}, "AsyncTableOperations.create_lookup_field": {"executed_lines": [559, 560, 571], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 494}, "AsyncTableOperations.create_alternate_key": {"executed_lines": [626, 627, 628, 629], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 575}, "AsyncTableOperations.get_alternate_keys": {"executed_lines": [660, 661, 662], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 638}, "AsyncTableOperations.delete_alternate_key": {"executed_lines": [690, 691], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 666}, "AsyncTableOperations.list_columns": {"executed_lines": [735, 736], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 695}, "AsyncTableOperations.list_relationships": {"executed_lines": [769, 770], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 740}, "AsyncTableOperations.list_table_relationships": {"executed_lines": [814, 815], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 774}, "": {"executed_lines": [6, 8, 10, 16, 17, 18, 19, 25, 28, 64, 69, 146, 168, 196, 249, 282, 315, 389, 443, 467, 494, 575, 638, 666, 695, 740, 774], "summary": {"covered_lines": 27, "num_statements": 27, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [21, 22], "start_line": 1}}, "classes": {"AsyncTableOperations": {"executed_lines": [65, 134, 135, 142, 163, 164, 188, 189, 190, 191, 192, 244, 245, 277, 278, 310, 311, 373, 374, 379, 429, 430, 434, 462, 463, 486, 487, 488, 489, 490, 559, 560, 571, 626, 627, 628, 629, 660, 661, 662, 690, 691, 735, 736, 769, 770, 814, 815], "summary": {"covered_lines": 48, "num_statements": 48, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 28}, "": {"executed_lines": [6, 8, 10, 16, 17, 18, 19, 25, 28, 64, 69, 146, 168, 196, 249, 282, 315, 389, 443, 467, 494, 575, 638, 666, 695, 740, 774], "summary": {"covered_lines": 27, "num_statements": 27, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [21, 22], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\claude_skill\\__init__.py": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\client.py": {"executed_lines": [4, 6, 7, 8, 10, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 25, 93, 99, 100, 101, 102, 103, 104, 105, 106, 109, 110, 111, 112, 113, 114, 116, 126, 127, 133, 135, 136, 138, 139, 140, 141, 145, 157, 158, 159, 160, 162, 168, 170, 187, 188, 189, 190, 191, 192, 193, 194, 195, 197, 199, 200, 203, 238, 244, 245, 246, 248, 296, 301, 303, 338, 343, 345, 428, 433, 434, 436, 447, 483, 488, 491, 514, 519, 521, 592, 597, 604, 625, 630, 632, 649, 654, 656, 688, 693, 695, 721, 726, 729, 761, 766, 777, 798, 799, 802], "summary": {"covered_lines": 105, "num_statements": 105, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"DataverseClient.__init__": {"executed_lines": [99, 100, 101, 102, 103, 104, 105, 106, 109, 110, 111, 112, 113, 114], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 93}, "DataverseClient._get_odata": {"executed_lines": [126, 127, 133], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 116}, "DataverseClient._scoped_odata": {"executed_lines": [138, 139, 140, 141], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 136}, "DataverseClient.__enter__": {"executed_lines": [157, 158, 159, 160], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 145}, "DataverseClient.__exit__": {"executed_lines": [168], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 162}, "DataverseClient.close": {"executed_lines": [187, 188, 189, 190, 191, 192, 193, 194, 195], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 170}, "DataverseClient._check_closed": {"executed_lines": [199, 200], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 197}, "DataverseClient.create": {"executed_lines": [238, 244, 245, 246], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 203}, "DataverseClient.update": {"executed_lines": [296, 301], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 248}, "DataverseClient.delete": {"executed_lines": [338, 343], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 303}, "DataverseClient.get": {"executed_lines": [428, 433, 434, 436], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 345}, "DataverseClient.query_sql": {"executed_lines": [483, 488], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 447}, "DataverseClient.get_table_info": {"executed_lines": [514, 519], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 491}, "DataverseClient.create_table": {"executed_lines": [592, 597], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 521}, "DataverseClient.delete_table": {"executed_lines": [625, 630], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 604}, "DataverseClient.list_tables": {"executed_lines": [649, 654], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 632}, "DataverseClient.create_columns": {"executed_lines": [688, 693], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 656}, "DataverseClient.delete_columns": {"executed_lines": [721, 726], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 695}, "DataverseClient.upload_file": {"executed_lines": [761, 766], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 729}, "DataverseClient.flush_cache": {"executed_lines": [798, 799], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 777}, "": {"executed_lines": [4, 6, 7, 8, 10, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 25, 93, 116, 135, 136, 145, 162, 170, 197, 203, 248, 303, 345, 447, 491, 521, 604, 632, 656, 695, 729, 777, 802], "summary": {"covered_lines": 38, "num_statements": 38, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"DataverseClient": {"executed_lines": [99, 100, 101, 102, 103, 104, 105, 106, 109, 110, 111, 112, 113, 114, 126, 127, 133, 138, 139, 140, 141, 157, 158, 159, 160, 168, 187, 188, 189, 190, 191, 192, 193, 194, 195, 199, 200, 238, 244, 245, 246, 296, 301, 338, 343, 428, 433, 434, 436, 483, 488, 514, 519, 592, 597, 625, 630, 649, 654, 688, 693, 721, 726, 761, 766, 798, 799], "summary": {"covered_lines": 67, "num_statements": 67, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 25}, "": {"executed_lines": [4, 6, 7, 8, 10, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 25, 93, 116, 135, 136, 145, 162, 170, 197, 203, 248, 303, 345, 447, 491, 521, 604, 632, 656, 695, 729, 777, 802], "summary": {"covered_lines": 38, "num_statements": 38, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\common\\__init__.py": {"executed_lines": [10], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [10], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [10], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\common\\constants.py": {"executed_lines": [12, 13, 14, 15, 16, 21, 22, 24, 25, 27, 28, 30, 31], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [12, 13, 14, 15, 16, 21, 22, 24, 25, 27, 28, 30, 31], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [12, 13, 14, 15, 16, 21, 22, 24, 25, 27, 28, 30, 31], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\core\\__init__.py": {"executed_lines": [11], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [11], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [11], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\core\\_auth.py": {"executed_lines": [12, 14, 16, 19, 20, 30, 31, 34, 43, 44, 45, 46, 48, 58, 59], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"_AuthManager.__init__": {"executed_lines": [44, 45, 46], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 43}, "_AuthManager._acquire_token": {"executed_lines": [58, 59], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 48}, "": {"executed_lines": [12, 14, 16, 19, 20, 30, 31, 34, 43, 48], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_TokenPair": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 20}, "_AuthManager": {"executed_lines": [44, 45, 46, 58, 59], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 34}, "": {"executed_lines": [12, 14, 16, 19, 20, 30, 31, 34, 43, 48], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\core\\_error_codes.py": {"executed_lines": [12, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 28, 44, 45, 46, 47, 48, 49, 50, 51, 54, 57, 58, 59, 60, 61, 62, 63, 66, 81, 84, 93, 96, 108], "summary": {"covered_lines": 36, "num_statements": 36, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"_http_subcode": {"executed_lines": [93], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 84}, "_is_transient_status": {"executed_lines": [108], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 96}, "": {"executed_lines": [12, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 28, 44, 45, 46, 47, 48, 49, 50, 51, 54, 57, 58, 59, 60, 61, 62, 63, 66, 81, 84, 96], "summary": {"covered_lines": 34, "num_statements": 34, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [12, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 28, 44, 45, 46, 47, 48, 49, 50, 51, 54, 57, 58, 59, 60, 61, 62, 63, 66, 81, 84, 93, 96, 108], "summary": {"covered_lines": 36, "num_statements": 36, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\core\\_http.py": {"executed_lines": [12, 14, 15, 17, 23, 45, 53, 54, 55, 56, 57, 59, 77, 78, 79, 81, 82, 86, 87, 88, 89, 92, 93, 101, 102, 103, 104, 105, 106, 108, 111, 112, 120, 121, 122, 123, 130, 131, 132, 133, 134, 136, 141, 142, 143], "summary": {"covered_lines": 45, "num_statements": 46, "percent_covered": 97.82608695652173, "percent_covered_display": "98", "missing_lines": 1, "excluded_lines": 2, "percent_statements_covered": 97.82608695652173, "percent_statements_covered_display": "98"}, "missing_lines": [90], "excluded_lines": [19, 20], "functions": {"_HttpClient.__init__": {"executed_lines": [53, 54, 55, 56, 57], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 45}, "_HttpClient._request": {"executed_lines": [77, 78, 79, 81, 82, 86, 87, 88, 89, 92, 93, 101, 102, 103, 104, 105, 106, 108, 111, 112, 120, 121, 122, 123, 130, 131, 132, 133, 134], "summary": {"covered_lines": 29, "num_statements": 30, "percent_covered": 96.66666666666667, "percent_covered_display": "97", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 96.66666666666667, "percent_statements_covered_display": "97"}, "missing_lines": [90], "excluded_lines": [], "start_line": 59}, "_HttpClient.close": {"executed_lines": [141, 142, 143], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 136}, "": {"executed_lines": [12, 14, 15, 17, 23, 45, 59, 136], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [19, 20], "start_line": 1}}, "classes": {"_HttpClient": {"executed_lines": [53, 54, 55, 56, 57, 77, 78, 79, 81, 82, 86, 87, 88, 89, 92, 93, 101, 102, 103, 104, 105, 106, 108, 111, 112, 120, 121, 122, 123, 130, 131, 132, 133, 134, 141, 142, 143], "summary": {"covered_lines": 37, "num_statements": 38, "percent_covered": 97.36842105263158, "percent_covered_display": "97", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 97.36842105263158, "percent_statements_covered_display": "97"}, "missing_lines": [90], "excluded_lines": [], "start_line": 23}, "": {"executed_lines": [12, 14, 15, 17, 23, 45, 59, 136], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [19, 20], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\core\\_http_logger.py": {"executed_lines": [8, 10, 11, 12, 13, 14, 15, 16, 18, 21, 24, 25, 26, 29, 33, 34, 35, 38, 39, 40, 41, 43, 49, 53, 54, 57, 62, 63, 75, 83, 84, 85, 86, 87, 88, 89, 91, 101, 102, 103, 104, 105, 106, 107, 108, 111, 112, 113, 114, 117, 118, 119, 121, 130, 131, 133, 134, 136, 138, 140, 141, 142, 144, 145, 147, 148, 149, 150, 151, 152, 153, 154, 158, 160, 161, 162, 163, 164, 167, 168, 169], "summary": {"covered_lines": 81, "num_statements": 85, "percent_covered": 95.29411764705883, "percent_covered_display": "95", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 95.29411764705883, "percent_statements_covered_display": "95"}, "missing_lines": [115, 116, 155, 156], "excluded_lines": [], "functions": {"_HttpLogger.__init__": {"executed_lines": [25, 26, 29, 33, 34, 35, 38, 39, 40, 41, 43, 49, 53, 54, 57, 62, 63], "summary": {"covered_lines": 17, "num_statements": 17, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 24}, "_HttpLogger.log_request": {"executed_lines": [83, 84, 85, 86, 87, 88, 89], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 75}, "_HttpLogger.log_response": {"executed_lines": [101, 102, 103, 104, 105, 106, 107, 108, 111, 112, 113, 114, 117, 118, 119], "summary": {"covered_lines": 15, "num_statements": 17, "percent_covered": 88.23529411764706, "percent_covered_display": "88", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 88.23529411764706, "percent_statements_covered_display": "88"}, "missing_lines": [115, 116], "excluded_lines": [], "start_line": 91}, "_HttpLogger.log_error": {"executed_lines": [130, 131], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 121}, "_HttpLogger.body_logging_enabled": {"executed_lines": [136], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 134}, "_HttpLogger.close": {"executed_lines": [140, 141, 142], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 138}, "_HttpLogger._redact_headers": {"executed_lines": [145], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 144}, "_HttpLogger._truncate_body": {"executed_lines": [148, 149, 150, 151, 152, 153, 154, 158, 160, 161, 162, 163, 164, 167, 168, 169], "summary": {"covered_lines": 16, "num_statements": 18, "percent_covered": 88.88888888888889, "percent_covered_display": "89", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 88.88888888888889, "percent_statements_covered_display": "89"}, "missing_lines": [155, 156], "excluded_lines": [], "start_line": 147}, "": {"executed_lines": [8, 10, 11, 12, 13, 14, 15, 16, 18, 21, 24, 75, 91, 121, 133, 134, 138, 144, 147], "summary": {"covered_lines": 19, "num_statements": 19, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_HttpLogger": {"executed_lines": [25, 26, 29, 33, 34, 35, 38, 39, 40, 41, 43, 49, 53, 54, 57, 62, 63, 83, 84, 85, 86, 87, 88, 89, 101, 102, 103, 104, 105, 106, 107, 108, 111, 112, 113, 114, 117, 118, 119, 130, 131, 136, 140, 141, 142, 145, 148, 149, 150, 151, 152, 153, 154, 158, 160, 161, 162, 163, 164, 167, 168, 169], "summary": {"covered_lines": 62, "num_statements": 66, "percent_covered": 93.93939393939394, "percent_covered_display": "94", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 93.93939393939394, "percent_statements_covered_display": "94"}, "missing_lines": [115, 116, 155, 156], "excluded_lines": [], "start_line": 21}, "": {"executed_lines": [8, 10, 11, 12, 13, 14, 15, 16, 18, 21, 24, 75, 91, 121, 133, 134, 138, 144, 147], "summary": {"covered_lines": 19, "num_statements": 19, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\core\\config.py": {"executed_lines": [12, 14, 15, 21, 22, 40, 43, 44, 45, 47, 49, 50, 58], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [17, 18], "functions": {"DataverseConfig.from_env": {"executed_lines": [58], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 50}, "": {"executed_lines": [12, 14, 15, 21, 22, 40, 43, 44, 45, 47, 49, 50], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [17, 18], "start_line": 1}}, "classes": {"DataverseConfig": {"executed_lines": [58], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 22}, "": {"executed_lines": [12, 14, 15, 21, 22, 40, 43, 44, 45, 47, 49, 50], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [17, 18], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\core\\errors.py": {"executed_lines": [15, 16, 17, 20, 40, 50, 51, 52, 53, 54, 55, 56, 57, 58, 60, 67, 82, 94, 95, 98, 110, 111, 114, 126, 127, 130, 160, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 201], "summary": {"covered_lines": 44, "num_statements": 44, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [78, 79], "functions": {"DataverseError.__init__": {"executed_lines": [50, 51, 52, 53, 54, 55, 56, 57, 58], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 40}, "DataverseError.to_dict": {"executed_lines": [67], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 60}, "DataverseError.__repr__": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 1, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [79], "start_line": 78}, "ValidationError.__init__": {"executed_lines": [95], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 94}, "MetadataError.__init__": {"executed_lines": [111], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 110}, "SQLParseError.__init__": {"executed_lines": [127], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 126}, "HttpError.__init__": {"executed_lines": [175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 160}, "": {"executed_lines": [15, 16, 17, 20, 40, 60, 82, 94, 98, 110, 114, 126, 130, 160, 201], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 1, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [78], "start_line": 1}}, "classes": {"DataverseError": {"executed_lines": [50, 51, 52, 53, 54, 55, 56, 57, 58, 67], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 1, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [79], "start_line": 20}, "ValidationError": {"executed_lines": [95], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 82}, "MetadataError": {"executed_lines": [111], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 98}, "SQLParseError": {"executed_lines": [127], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 114}, "HttpError": {"executed_lines": [175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 130}, "": {"executed_lines": [15, 16, 17, 20, 40, 60, 82, 94, 98, 110, 114, 126, 130, 160, 201], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 1, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [78], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\core\\log_config.py": {"executed_lines": [11, 13, 14, 16, 18, 21, 31, 32, 35, 36, 61, 62, 63, 67, 68, 69, 72, 73, 74, 75], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"_default_redacted_headers": {"executed_lines": [32], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 31}, "LogConfig.__post_init__": {"executed_lines": [62, 63], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 61}, "": {"executed_lines": [11, 13, 14, 16, 18, 21, 31, 35, 36, 61, 67, 68, 69, 72, 73, 74, 75], "summary": {"covered_lines": 17, "num_statements": 17, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"LogConfig": {"executed_lines": [62, 63], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 36}, "": {"executed_lines": [11, 13, 14, 16, 18, 21, 31, 32, 35, 36, 61, 67, 68, 69, 72, 73, 74, 75], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\data\\__init__.py": {"executed_lines": [11], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [11], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [11], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\data\\_batch.py": {"executed_lines": [6, 8, 9, 11, 12, 13, 14, 15, 42, 50, 62, 73, 74, 76, 78, 79, 80, 87, 88, 90, 93, 94, 96, 97, 108, 114, 115, 116, 117, 118, 120, 121, 122, 124, 125, 127, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 168, 170, 171, 172, 176, 182, 183, 184, 185, 186, 188, 189, 190, 191, 192, 193, 194, 196, 197, 198, 199, 200, 201, 202, 203, 204, 206, 207, 209, 210, 211, 212, 213, 214, 215, 216, 224, 226, 227, 228, 232, 234, 235, 236, 238, 239, 240, 242, 243, 244, 245, 246, 247, 250, 251, 255, 256, 262, 263], "summary": {"covered_lines": 130, "num_statements": 130, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [39, 40], "functions": {"_BatchClient.execute": {"executed_lines": [73, 74, 76, 78, 79, 80, 87, 88, 90, 93, 94, 96, 97, 108], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 62}, "_BatchClient._resolve_all": {"executed_lines": [115, 116, 117, 118, 120, 121, 122, 124, 125], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 114}, "_BatchClient._resolve_item": {"executed_lines": [129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163], "summary": {"covered_lines": 35, "num_statements": 35, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 127}, "_BatchClient._resolve_one": {"executed_lines": [170, 171, 172, 176], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 168}, "_BatchClient._resolve_record_create": {"executed_lines": [183, 184, 185, 186], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 182}, "_BatchClient._resolve_record_update": {"executed_lines": [189, 190, 191, 192, 193, 194], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 188}, "_BatchClient._resolve_record_delete": {"executed_lines": [197, 198, 199, 200, 201, 202, 203, 204], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 196}, "_BatchClient._resolve_record_get": {"executed_lines": [207], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 206}, "_BatchClient._resolve_record_upsert": {"executed_lines": [210, 211, 212, 213, 214, 215, 216], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 209}, "_BatchClient._require_entity_metadata": {"executed_lines": [226, 227, 228, 232], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 224}, "_BatchClient._resolve_table_delete": {"executed_lines": [235, 236], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 234}, "_BatchClient._resolve_table_add_columns": {"executed_lines": [239, 240], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 238}, "_BatchClient._resolve_table_remove_columns": {"executed_lines": [243, 244, 245, 246, 247, 250, 251, 255, 256], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 242}, "_BatchClient._resolve_query_sql": {"executed_lines": [263], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 262}, "": {"executed_lines": [6, 8, 9, 11, 12, 13, 14, 15, 42, 50, 62, 114, 127, 168, 182, 188, 196, 206, 209, 224, 234, 238, 242, 262], "summary": {"covered_lines": 24, "num_statements": 24, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [39, 40], "start_line": 1}}, "classes": {"_BatchClient": {"executed_lines": [73, 74, 76, 78, 79, 80, 87, 88, 90, 93, 94, 96, 97, 108, 115, 116, 117, 118, 120, 121, 122, 124, 125, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 170, 171, 172, 176, 183, 184, 185, 186, 189, 190, 191, 192, 193, 194, 197, 198, 199, 200, 201, 202, 203, 204, 207, 210, 211, 212, 213, 214, 215, 216, 226, 227, 228, 232, 235, 236, 239, 240, 243, 244, 245, 246, 247, 250, 251, 255, 256, 263], "summary": {"covered_lines": 106, "num_statements": 106, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 50}, "": {"executed_lines": [6, 8, 9, 11, 12, 13, 14, 15, 42, 50, 62, 114, 127, 168, 182, 188, 196, 206, 209, 224, 234, 238, 242, 262], "summary": {"covered_lines": 24, "num_statements": 24, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [39, 40], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\data\\_batch_base.py": {"executed_lines": [9, 11, 12, 13, 14, 15, 17, 18, 19, 20, 25, 26, 27, 28, 33, 35, 36, 47, 48, 49, 50, 51, 54, 55, 56, 57, 58, 59, 62, 63, 64, 65, 66, 67, 70, 71, 72, 73, 74, 77, 78, 79, 80, 86, 87, 88, 89, 90, 91, 92, 95, 96, 97, 100, 101, 102, 105, 106, 107, 108, 111, 112, 113, 114, 117, 118, 119, 120, 123, 124, 125, 126, 127, 130, 131, 132, 133, 136, 137, 138, 141, 142, 143, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 162, 163, 164, 172, 173, 186, 187, 189, 191, 192, 193, 194, 196, 198, 199, 200, 202, 204, 205, 206, 216, 217, 220, 228, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 253, 256, 257, 258, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 283, 284, 285, 287, 288, 289, 290, 291, 292, 293, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 344, 345, 346, 347, 362, 372, 373, 379, 380, 382, 383, 385, 386, 388, 389, 390, 391, 393, 394, 396, 397, 399, 400, 402, 403, 413, 414, 415, 421, 426, 427, 428, 429, 431, 432, 434, 436, 441, 442, 444, 445, 446, 447, 448, 449, 450, 451, 452, 454, 455, 456, 458, 459, 460, 461, 462, 464, 467, 473, 474, 475, 476, 481, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 495, 496, 497, 498], "summary": {"covered_lines": 287, "num_statements": 288, "percent_covered": 99.65277777777777, "percent_covered_display": "99", "missing_lines": 1, "excluded_lines": 2, "percent_statements_covered": 99.65277777777777, "percent_statements_covered_display": "99"}, "missing_lines": [482], "excluded_lines": [30, 31], "functions": {"_ChangeSet.add_create": {"executed_lines": [191, 192, 193, 194], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 189}, "_ChangeSet.add_update": {"executed_lines": [198, 199, 200], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 196}, "_ChangeSet.add_delete": {"executed_lines": [204, 205, 206], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 202}, "_raise_top_level_batch_error": {"executed_lines": [236, 237, 238, 239, 240, 241, 242, 243, 244, 245], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 228}, "_extract_boundary": {"executed_lines": [257, 258], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 256}, "_split_multipart": {"executed_lines": [262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280], "summary": {"covered_lines": 19, "num_statements": 19, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 261}, "_parse_mime_part": {"executed_lines": [284, 285, 287, 288, 289, 290, 291, 292, 293], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 283}, "_parse_http_response_part": {"executed_lines": [297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 344, 345, 346, 347], "summary": {"covered_lines": 50, "num_statements": 50, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 296}, "_BatchBase.__init__": {"executed_lines": [373], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 372}, "_BatchBase._resolve_table_create": {"executed_lines": [380], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 379}, "_BatchBase._resolve_table_get": {"executed_lines": [383], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 382}, "_BatchBase._resolve_table_list": {"executed_lines": [386], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 385}, "_BatchBase._resolve_table_create_one_to_many": {"executed_lines": [389, 390, 391], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 388}, "_BatchBase._resolve_table_create_many_to_many": {"executed_lines": [394], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 393}, "_BatchBase._resolve_table_delete_relationship": {"executed_lines": [397], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 396}, "_BatchBase._resolve_table_get_relationship": {"executed_lines": [400], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 399}, "_BatchBase._resolve_table_create_lookup_field": {"executed_lines": [403, 413, 414, 415], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 402}, "_BatchBase._build_batch_body": {"executed_lines": [426, 427, 428, 429, 431, 432], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 421}, "_BatchBase._serialize_raw_request": {"executed_lines": [436, 441, 442, 444, 445, 446, 447, 448, 449, 450, 451, 452, 454, 455, 456], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 434}, "_BatchBase._serialize_changeset_item": {"executed_lines": [459, 460, 461, 462, 464, 467], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 458}, "_BatchBase._parse_batch_response": {"executed_lines": [474, 475, 476, 481, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 495, 496, 497, 498], "summary": {"covered_lines": 19, "num_statements": 20, "percent_covered": 95.0, "percent_covered_display": "95", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 95.0, "percent_statements_covered_display": "95"}, "missing_lines": [482], "excluded_lines": [], "start_line": 473}, "": {"executed_lines": [9, 11, 12, 13, 14, 15, 17, 18, 19, 20, 25, 26, 27, 28, 33, 35, 36, 47, 48, 49, 50, 51, 54, 55, 56, 57, 58, 59, 62, 63, 64, 65, 66, 67, 70, 71, 72, 73, 74, 77, 78, 79, 80, 86, 87, 88, 89, 90, 91, 92, 95, 96, 97, 100, 101, 102, 105, 106, 107, 108, 111, 112, 113, 114, 117, 118, 119, 120, 123, 124, 125, 126, 127, 130, 131, 132, 133, 136, 137, 138, 141, 142, 143, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 162, 163, 164, 172, 173, 186, 187, 189, 196, 202, 216, 217, 220, 228, 253, 256, 261, 283, 296, 362, 372, 379, 382, 385, 388, 393, 396, 399, 402, 421, 434, 458, 473], "summary": {"covered_lines": 127, "num_statements": 127, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [30, 31], "start_line": 1}}, "classes": {"_RecordCreate": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 48}, "_RecordUpdate": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 55}, "_RecordDelete": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 63}, "_RecordGet": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 71}, "_RecordUpsert": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 78}, "_TableCreate": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 87}, "_TableDelete": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 96}, "_TableGet": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 101}, "_TableList": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 106}, "_TableAddColumns": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 112}, "_TableRemoveColumns": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 118}, "_TableCreateOneToMany": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 124}, "_TableCreateManyToMany": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 131}, "_TableDeleteRelationship": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 137}, "_TableGetRelationship": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 142}, "_TableCreateLookupField": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 147}, "_QuerySql": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 163}, "_ChangeSet": {"executed_lines": [191, 192, 193, 194, 198, 199, 200, 204, 205, 206], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 173}, "_ChangeSetBatchItem": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 217}, "_BatchBase": {"executed_lines": [373, 380, 383, 386, 389, 390, 391, 394, 397, 400, 403, 413, 414, 415, 426, 427, 428, 429, 431, 432, 436, 441, 442, 444, 445, 446, 447, 448, 449, 450, 451, 452, 454, 455, 456, 459, 460, 461, 462, 464, 467, 474, 475, 476, 481, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 495, 496, 497, 498], "summary": {"covered_lines": 60, "num_statements": 61, "percent_covered": 98.36065573770492, "percent_covered_display": "98", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 98.36065573770492, "percent_statements_covered_display": "98"}, "missing_lines": [482], "excluded_lines": [], "start_line": 362}, "": {"executed_lines": [9, 11, 12, 13, 14, 15, 17, 18, 19, 20, 25, 26, 27, 28, 33, 35, 36, 47, 48, 49, 50, 51, 54, 55, 56, 57, 58, 59, 62, 63, 64, 65, 66, 67, 70, 71, 72, 73, 74, 77, 78, 79, 80, 86, 87, 88, 89, 90, 91, 92, 95, 96, 97, 100, 101, 102, 105, 106, 107, 108, 111, 112, 113, 114, 117, 118, 119, 120, 123, 124, 125, 126, 127, 130, 131, 132, 133, 136, 137, 138, 141, 142, 143, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 162, 163, 164, 172, 173, 186, 187, 189, 196, 202, 216, 217, 220, 228, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 253, 256, 257, 258, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 283, 284, 285, 287, 288, 289, 290, 291, 292, 293, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 344, 345, 346, 347, 362, 372, 379, 382, 385, 388, 393, 396, 399, 402, 421, 434, 458, 473], "summary": {"covered_lines": 217, "num_statements": 217, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [30, 31], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\data\\_odata.py": {"executed_lines": [6, 8, 10, 11, 12, 13, 14, 16, 18, 19, 20, 21, 22, 23, 36, 46, 49, 70, 71, 72, 80, 86, 87, 88, 90, 92, 93, 94, 103, 104, 105, 106, 107, 108, 109, 111, 112, 114, 115, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 140, 141, 142, 143, 144, 145, 146, 147, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 179, 185, 186, 187, 188, 189, 190, 193, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 225, 241, 242, 243, 244, 245, 246, 247, 248, 249, 251, 252, 253, 255, 256, 258, 259, 260, 262, 263, 264, 265, 266, 267, 269, 295, 296, 297, 298, 299, 301, 332, 333, 336, 337, 338, 339, 340, 341, 342, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 357, 359, 360, 361, 362, 364, 365, 366, 367, 368, 372, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 409, 424, 425, 426, 427, 431, 432, 433, 434, 435, 436, 437, 438, 440, 452, 454, 472, 473, 474, 475, 477, 488, 490, 503, 505, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 553, 554, 555, 556, 557, 558, 559, 561, 562, 563, 564, 566, 567, 569, 570, 572, 573, 575, 576, 577, 578, 579, 581, 582, 583, 584, 586, 587, 588, 590, 591, 592, 593, 594, 595, 598, 617, 618, 619, 620, 621, 626, 628, 629, 630, 631, 632, 635, 636, 637, 638, 639, 641, 642, 643, 646, 647, 648, 649, 650, 652, 653, 662, 663, 667, 668, 669, 670, 679, 680, 681, 682, 683, 684, 691, 692, 693, 694, 695, 702, 703, 704, 705, 706, 707, 708, 709, 710, 712, 715, 720, 721, 724, 725, 726, 727, 728, 730, 731, 732, 736, 737, 738, 739, 740, 741, 742, 743, 748, 752, 753, 754, 755, 759, 760, 761, 762, 763, 766, 777, 779, 780, 781, 785, 786, 787, 789, 796, 797, 809, 810, 811, 812, 813, 817, 818, 821, 822, 823, 825, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843, 844, 845, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859, 861, 889, 890, 891, 895, 896, 897, 898, 899, 900, 901, 902, 903, 905, 918, 919, 920, 921, 923, 924, 925, 926, 927, 928, 929, 930, 931, 934, 939, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 954, 962, 963, 964, 965, 966, 968, 969, 974, 975, 976, 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, 990, 992, 993, 995, 996, 997, 998, 999, 1000, 1001, 1002, 1003, 1004, 1006, 1008, 1017, 1020, 1024, 1025, 1028, 1031, 1032, 1033, 1035, 1037, 1038, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1049, 1050, 1052, 1061, 1062, 1063, 1064, 1074, 1101, 1103, 1115, 1116, 1117, 1121, 1125, 1152, 1153, 1154, 1159, 1160, 1161, 1165, 1166, 1167, 1168, 1170, 1176, 1190, 1191, 1192, 1197, 1198, 1199, 1200, 1202, 1218, 1219, 1220, 1225, 1226, 1227, 1229, 1259, 1260, 1261, 1266, 1270, 1271, 1273, 1277, 1278, 1279, 1280, 1281, 1282, 1283, 1284, 1286, 1287, 1288, 1289, 1290, 1292, 1293, 1294, 1296, 1303, 1313, 1333, 1334, 1336, 1337, 1338, 1343, 1344, 1345, 1347, 1348, 1349, 1350, 1354, 1355, 1356, 1361, 1362, 1364, 1365, 1367, 1369, 1390, 1391, 1392, 1393, 1395, 1397, 1398, 1399, 1401, 1402, 1403, 1409, 1410, 1411, 1412, 1414, 1415, 1416, 1417, 1422, 1423, 1424, 1426, 1428, 1429, 1430, 1431, 1432, 1434, 1436, 1437, 1439, 1443, 1452, 1453, 1454, 1461, 1468, 1470, 1471, 1472, 1473, 1474, 1475, 1476, 1477, 1478, 1484, 1497, 1498, 1499, 1502, 1503, 1504, 1512, 1524, 1525, 1526, 1527, 1528, 1529, 1530, 1531, 1532, 1538, 1560, 1582, 1590, 1591, 1595, 1596, 1597, 1598, 1599, 1600, 1601, 1604, 1605, 1609, 1610, 1611, 1612, 1613, 1614, 1620, 1631, 1634, 1635, 1636, 1643, 1645, 1646, 1647, 1648, 1679, 1685, 1693, 1694, 1695, 1696, 1697, 1699, 1714, 1715, 1716], "summary": {"covered_lines": 670, "num_statements": 690, "percent_covered": 97.10144927536231, "percent_covered_display": "97", "missing_lines": 20, "excluded_lines": 0, "percent_statements_covered": 97.10144927536231, "percent_statements_covered_display": "97"}, "missing_lines": [991, 994, 1034, 1469, 1500, 1546, 1547, 1548, 1549, 1550, 1551, 1555, 1557, 1558, 1572, 1573, 1574, 1575, 1576, 1632], "excluded_lines": [], "functions": {"_ODataClient.__init__": {"executed_lines": [70, 71, 72], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 49}, "_ODataClient.close": {"executed_lines": [86, 87, 88], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 80}, "_ODataClient._headers": {"executed_lines": [92, 93, 94], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 90}, "_ODataClient._merge_headers": {"executed_lines": [104, 105, 106, 107, 108, 109], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 103}, "_ODataClient._raw_request": {"executed_lines": [112], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 111}, "_ODataClient._request": {"executed_lines": [115, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 140, 141, 142, 143, 144, 145, 146, 147, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161], "summary": {"covered_lines": 35, "num_statements": 35, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 114}, "_ODataClient._execute_raw": {"executed_lines": [185, 186, 187, 188, 189, 190], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 179}, "_ODataClient._create": {"executed_lines": [209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 193}, "_ODataClient._create_multiple": {"executed_lines": [241, 242, 243, 244, 245, 246, 247, 248, 249, 251, 252, 253, 255, 256, 258, 259, 260, 262, 263, 264, 265, 266, 267], "summary": {"covered_lines": 23, "num_statements": 23, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 225}, "_ODataClient._upsert": {"executed_lines": [295, 296, 297, 298, 299], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 269}, "_ODataClient._upsert_multiple": {"executed_lines": [332, 333, 336, 337, 338, 339, 340, 341, 342, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354], "summary": {"covered_lines": 19, "num_statements": 19, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 301}, "_ODataClient._primary_id_attr": {"executed_lines": [359, 360, 361, 362, 364, 365, 366, 367, 368], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 357}, "_ODataClient._update_by_ids": {"executed_lines": [387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407], "summary": {"covered_lines": 21, "num_statements": 21, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 372}, "_ODataClient._delete_multiple": {"executed_lines": [424, 425, 426, 427, 431, 432, 433, 434, 435, 436, 437, 438], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 409}, "_ODataClient._update": {"executed_lines": [452], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 440}, "_ODataClient._update_multiple": {"executed_lines": [472, 473, 474, 475], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 454}, "_ODataClient._delete": {"executed_lines": [488], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 477}, "_ODataClient._get": {"executed_lines": [503], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 490}, "_ODataClient._get_multiple": {"executed_lines": [542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 553, 561, 562, 563, 564, 566, 567, 569, 570, 572, 573, 575, 576, 577, 578, 579, 581, 582, 583, 584, 586, 587, 588, 590, 591, 592, 593, 594, 595], "summary": {"covered_lines": 39, "num_statements": 39, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 505}, "_ODataClient._get_multiple._do_request": {"executed_lines": [554, 555, 556, 557, 558, 559], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 553}, "_ODataClient._query_sql": {"executed_lines": [617, 618, 619, 620, 621, 626, 628, 629, 630, 631, 632, 635, 636, 637, 638, 639, 641, 642, 643, 646, 647, 648, 649, 650, 652, 653, 662, 663, 667, 668, 669, 670, 679, 680, 681, 682, 683, 684, 691, 692, 693, 694, 695, 702, 703, 704, 705, 706, 707, 708, 709, 710, 712], "summary": {"covered_lines": 53, "num_statements": 53, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 598}, "_ODataClient._entity_set_from_schema_name": {"executed_lines": [720, 721, 724, 725, 726, 727, 728, 730, 731, 732, 736, 737, 738, 739, 740, 741, 742, 743, 748, 752, 753, 754, 755, 759, 760, 761, 762, 763], "summary": {"covered_lines": 28, "num_statements": 28, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 715}, "_ODataClient._get_entity_by_table_schema_name": {"executed_lines": [777, 779, 780, 781, 785, 786, 787], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 766}, "_ODataClient._create_entity": {"executed_lines": [796, 797, 809, 810, 811, 812, 813, 817, 818, 821, 822, 823], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 789}, "_ODataClient._get_attribute_metadata": {"executed_lines": [832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843, 844, 845, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859], "summary": {"covered_lines": 25, "num_statements": 25, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 825}, "_ODataClient._list_columns": {"executed_lines": [889, 890, 891, 895, 896, 897, 898, 899, 900, 901, 902, 903], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 861}, "_ODataClient._wait_for_attribute_visibility": {"executed_lines": [918, 919, 920, 921, 923, 924, 925, 926, 927, 928, 929, 930, 931, 934], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 905}, "_ODataClient._request_metadata_with_retry": {"executed_lines": [941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 939}, "_ODataClient._bulk_fetch_picklists": {"executed_lines": [962, 963, 964, 965, 966, 968, 969, 974, 975, 976, 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, 990, 992, 993, 995, 996, 997, 998, 999, 1000, 1001, 1002, 1003, 1004, 1006], "summary": {"covered_lines": 36, "num_statements": 38, "percent_covered": 94.73684210526316, "percent_covered_display": "95", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 94.73684210526316, "percent_statements_covered_display": "95"}, "missing_lines": [991, 994], "excluded_lines": [], "start_line": 954}, "_ODataClient._convert_labels_to_ints": {"executed_lines": [1017, 1020, 1024, 1025, 1028, 1031, 1032, 1033, 1035, 1037, 1038, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1049, 1050], "summary": {"covered_lines": 23, "num_statements": 24, "percent_covered": 95.83333333333333, "percent_covered_display": "96", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 95.83333333333333, "percent_statements_covered_display": "96"}, "missing_lines": [1034], "excluded_lines": [], "start_line": 1008}, "_ODataClient._get_table_info": {"executed_lines": [1061, 1062, 1063, 1064], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1052}, "_ODataClient._list_tables": {"executed_lines": [1101], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1074}, "_ODataClient._delete_table": {"executed_lines": [1115, 1116, 1117, 1121], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1103}, "_ODataClient._create_alternate_key": {"executed_lines": [1152, 1153, 1154, 1159, 1160, 1161, 1165, 1166, 1167, 1168, 1170], "summary": {"covered_lines": 11, "num_statements": 11, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1125}, "_ODataClient._get_alternate_keys": {"executed_lines": [1190, 1191, 1192, 1197, 1198, 1199, 1200], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1176}, "_ODataClient._delete_alternate_key": {"executed_lines": [1218, 1219, 1220, 1225, 1226, 1227], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1202}, "_ODataClient._create_table": {"executed_lines": [1259, 1260, 1261, 1266, 1270, 1271, 1273, 1277, 1278, 1279, 1280, 1281, 1282, 1283, 1284, 1286, 1287, 1288, 1289, 1290, 1292, 1293, 1294, 1296, 1303], "summary": {"covered_lines": 25, "num_statements": 25, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1229}, "_ODataClient._create_columns": {"executed_lines": [1333, 1334, 1336, 1337, 1338, 1343, 1344, 1345, 1347, 1348, 1349, 1350, 1354, 1355, 1356, 1361, 1362, 1364, 1365, 1367], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1313}, "_ODataClient._delete_columns": {"executed_lines": [1390, 1391, 1392, 1393, 1395, 1397, 1398, 1399, 1401, 1402, 1403, 1409, 1410, 1411, 1412, 1414, 1415, 1416, 1417, 1422, 1423, 1424, 1426, 1428, 1429, 1430, 1431, 1432, 1434, 1436, 1437, 1439], "summary": {"covered_lines": 32, "num_statements": 32, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1369}, "_ODataClient._build_create": {"executed_lines": [1452, 1453, 1454], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1443}, "_ODataClient._build_create_multiple": {"executed_lines": [1468, 1470, 1471, 1472, 1473, 1474, 1475, 1476, 1477, 1478], "summary": {"covered_lines": 10, "num_statements": 11, "percent_covered": 90.9090909090909, "percent_covered_display": "91", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 90.9090909090909, "percent_statements_covered_display": "91"}, "missing_lines": [1469], "excluded_lines": [], "start_line": 1461}, "_ODataClient._build_update": {"executed_lines": [1497, 1498, 1499, 1502, 1503, 1504], "summary": {"covered_lines": 6, "num_statements": 7, "percent_covered": 85.71428571428571, "percent_covered_display": "86", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 85.71428571428571, "percent_statements_covered_display": "86"}, "missing_lines": [1500], "excluded_lines": [], "start_line": 1484}, "_ODataClient._build_update_multiple_from_records": {"executed_lines": [1524, 1525, 1526, 1527, 1528, 1529, 1530, 1531, 1532], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1512}, "_ODataClient._build_update_multiple": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 9, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 9, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1546, 1547, 1548, 1549, 1550, 1551, 1555, 1557, 1558], "excluded_lines": [], "start_line": 1538}, "_ODataClient._build_upsert": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 5, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 5, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1572, 1573, 1574, 1575, 1576], "excluded_lines": [], "start_line": 1560}, "_ODataClient._build_upsert_multiple": {"executed_lines": [1590, 1591, 1595, 1596, 1597, 1598, 1599, 1600, 1601, 1604, 1605, 1609, 1610, 1611, 1612, 1613, 1614], "summary": {"covered_lines": 17, "num_statements": 17, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1582}, "_ODataClient._build_delete": {"executed_lines": [1631, 1634, 1635, 1636], "summary": {"covered_lines": 4, "num_statements": 5, "percent_covered": 80.0, "percent_covered_display": "80", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 80.0, "percent_statements_covered_display": "80"}, "missing_lines": [1632], "excluded_lines": [], "start_line": 1620}, "_ODataClient._build_delete_multiple": {"executed_lines": [1645, 1646, 1647, 1648, 1679], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1643}, "_ODataClient._build_get": {"executed_lines": [1693, 1694, 1695, 1696, 1697], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1685}, "_ODataClient._build_sql": {"executed_lines": [1714, 1715, 1716], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1699}, "": {"executed_lines": [6, 8, 10, 11, 12, 13, 14, 16, 18, 19, 20, 21, 22, 23, 36, 46, 49, 80, 90, 103, 111, 114, 179, 193, 225, 269, 301, 357, 372, 409, 440, 454, 477, 490, 505, 598, 715, 766, 789, 825, 861, 905, 939, 954, 1008, 1052, 1074, 1103, 1125, 1176, 1202, 1229, 1313, 1369, 1443, 1461, 1484, 1512, 1538, 1560, 1582, 1620, 1643, 1685, 1699], "summary": {"covered_lines": 65, "num_statements": 65, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_ODataClient": {"executed_lines": [70, 71, 72, 86, 87, 88, 92, 93, 94, 104, 105, 106, 107, 108, 109, 112, 115, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 140, 141, 142, 143, 144, 145, 146, 147, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 185, 186, 187, 188, 189, 190, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 241, 242, 243, 244, 245, 246, 247, 248, 249, 251, 252, 253, 255, 256, 258, 259, 260, 262, 263, 264, 265, 266, 267, 295, 296, 297, 298, 299, 332, 333, 336, 337, 338, 339, 340, 341, 342, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 359, 360, 361, 362, 364, 365, 366, 367, 368, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 424, 425, 426, 427, 431, 432, 433, 434, 435, 436, 437, 438, 452, 472, 473, 474, 475, 488, 503, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 553, 554, 555, 556, 557, 558, 559, 561, 562, 563, 564, 566, 567, 569, 570, 572, 573, 575, 576, 577, 578, 579, 581, 582, 583, 584, 586, 587, 588, 590, 591, 592, 593, 594, 595, 617, 618, 619, 620, 621, 626, 628, 629, 630, 631, 632, 635, 636, 637, 638, 639, 641, 642, 643, 646, 647, 648, 649, 650, 652, 653, 662, 663, 667, 668, 669, 670, 679, 680, 681, 682, 683, 684, 691, 692, 693, 694, 695, 702, 703, 704, 705, 706, 707, 708, 709, 710, 712, 720, 721, 724, 725, 726, 727, 728, 730, 731, 732, 736, 737, 738, 739, 740, 741, 742, 743, 748, 752, 753, 754, 755, 759, 760, 761, 762, 763, 777, 779, 780, 781, 785, 786, 787, 796, 797, 809, 810, 811, 812, 813, 817, 818, 821, 822, 823, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843, 844, 845, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859, 889, 890, 891, 895, 896, 897, 898, 899, 900, 901, 902, 903, 918, 919, 920, 921, 923, 924, 925, 926, 927, 928, 929, 930, 931, 934, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 962, 963, 964, 965, 966, 968, 969, 974, 975, 976, 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, 990, 992, 993, 995, 996, 997, 998, 999, 1000, 1001, 1002, 1003, 1004, 1006, 1017, 1020, 1024, 1025, 1028, 1031, 1032, 1033, 1035, 1037, 1038, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1049, 1050, 1061, 1062, 1063, 1064, 1101, 1115, 1116, 1117, 1121, 1152, 1153, 1154, 1159, 1160, 1161, 1165, 1166, 1167, 1168, 1170, 1190, 1191, 1192, 1197, 1198, 1199, 1200, 1218, 1219, 1220, 1225, 1226, 1227, 1259, 1260, 1261, 1266, 1270, 1271, 1273, 1277, 1278, 1279, 1280, 1281, 1282, 1283, 1284, 1286, 1287, 1288, 1289, 1290, 1292, 1293, 1294, 1296, 1303, 1333, 1334, 1336, 1337, 1338, 1343, 1344, 1345, 1347, 1348, 1349, 1350, 1354, 1355, 1356, 1361, 1362, 1364, 1365, 1367, 1390, 1391, 1392, 1393, 1395, 1397, 1398, 1399, 1401, 1402, 1403, 1409, 1410, 1411, 1412, 1414, 1415, 1416, 1417, 1422, 1423, 1424, 1426, 1428, 1429, 1430, 1431, 1432, 1434, 1436, 1437, 1439, 1452, 1453, 1454, 1468, 1470, 1471, 1472, 1473, 1474, 1475, 1476, 1477, 1478, 1497, 1498, 1499, 1502, 1503, 1504, 1524, 1525, 1526, 1527, 1528, 1529, 1530, 1531, 1532, 1590, 1591, 1595, 1596, 1597, 1598, 1599, 1600, 1601, 1604, 1605, 1609, 1610, 1611, 1612, 1613, 1614, 1631, 1634, 1635, 1636, 1645, 1646, 1647, 1648, 1679, 1693, 1694, 1695, 1696, 1697, 1714, 1715, 1716], "summary": {"covered_lines": 605, "num_statements": 625, "percent_covered": 96.8, "percent_covered_display": "97", "missing_lines": 20, "excluded_lines": 0, "percent_statements_covered": 96.8, "percent_statements_covered_display": "97"}, "missing_lines": [991, 994, 1034, 1469, 1500, 1546, 1547, 1548, 1549, 1550, 1551, 1555, 1557, 1558, 1572, 1573, 1574, 1575, 1576, 1632], "excluded_lines": [], "start_line": 46}, "": {"executed_lines": [6, 8, 10, 11, 12, 13, 14, 16, 18, 19, 20, 21, 22, 23, 36, 46, 49, 80, 90, 103, 111, 114, 179, 193, 225, 269, 301, 357, 372, 409, 440, 454, 477, 490, 505, 598, 715, 766, 789, 825, 861, 905, 939, 954, 1008, 1052, 1074, 1103, 1125, 1176, 1202, 1229, 1313, 1369, 1443, 1461, 1484, 1512, 1538, 1560, 1582, 1620, 1643, 1685, 1699], "summary": {"covered_lines": 65, "num_statements": 65, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\data\\_odata_base.py": {"executed_lines": [10, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24, 26, 27, 33, 38, 39, 40, 42, 44, 45, 46, 47, 50, 60, 61, 62, 63, 64, 70, 71, 72, 73, 74, 75, 78, 79, 82, 83, 84, 85, 86, 88, 89, 98, 99, 100, 101, 102, 103, 112, 120, 130, 131, 133, 134, 140, 142, 143, 144, 145, 146, 155, 156, 158, 160, 161, 163, 165, 166, 179, 180, 181, 183, 184, 189, 190, 191, 193, 194, 202, 203, 205, 206, 207, 208, 209, 210, 216, 217, 219, 220, 221, 222, 224, 226, 227, 228, 229, 231, 233, 235, 237, 238, 239, 240, 241, 243, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 270, 271, 273, 274, 275, 286, 287, 288, 290, 292, 295, 296, 298, 299, 305, 310, 311, 312, 313, 314, 315, 316, 323, 324, 325, 330, 343, 344, 345, 348, 349, 350, 352, 353, 356, 358, 360, 361, 362, 364, 365, 366, 367, 368, 370, 371, 372, 373, 374, 375, 376, 377, 379, 380, 381, 382, 383, 384, 386, 387, 388, 389, 391, 393, 395, 396, 397, 398, 399, 400, 401, 409, 410, 423, 427, 428, 429, 430, 433, 434, 435, 436, 445, 446, 455, 456, 465, 466, 475, 476, 485, 486, 494, 495, 513, 514, 520, 526, 535, 536, 538, 539, 540, 541, 542, 543, 547, 548, 549, 550, 551, 552, 564, 565, 566, 567, 573, 575, 581, 583, 584, 593, 600, 601, 602, 604, 605, 606, 607, 608, 609, 610, 612, 631, 637, 643, 644, 668, 669, 671, 683, 684, 687, 688, 695, 697, 714, 722, 735, 739, 740, 741, 746, 750, 751, 752, 753, 761, 766, 800, 801, 802, 810, 811, 812, 820, 821, 829, 830, 837, 838, 845, 846, 858, 859, 869, 870, 880, 881, 891, 897, 902, 903, 904, 905, 913, 925, 926, 927, 932, 933, 934], "summary": {"covered_lines": 312, "num_statements": 329, "percent_covered": 94.83282674772036, "percent_covered_display": "95", "missing_lines": 17, "excluded_lines": 0, "percent_statements_covered": 94.83282674772036, "percent_statements_covered_display": "95"}, "missing_lines": [132, 147, 149, 293, 619, 620, 621, 625, 704, 705, 706, 707, 716, 724, 725, 906, 907], "excluded_lines": [], "functions": {"_extract_pagingcookie": {"executed_lines": [60, 61, 62, 63, 64, 70, 71, 72, 73, 74, 75], "summary": {"covered_lines": 11, "num_statements": 11, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 50}, "_RequestContext.build": {"executed_lines": [98, 99, 100, 101, 102, 103], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 89}, "_ODataBase.__init__": {"executed_lines": [130, 131, 133, 134, 140, 142, 143, 144, 145, 146], "summary": {"covered_lines": 10, "num_statements": 13, "percent_covered": 76.92307692307692, "percent_covered_display": "77", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 76.92307692307692, "percent_statements_covered_display": "77"}, "missing_lines": [132, 147, 149], "excluded_lines": [], "start_line": 120}, "_ODataBase._escape_odata_quotes": {"executed_lines": [158], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 156}, "_ODataBase._normalize_cache_key": {"executed_lines": [163], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 161}, "_ODataBase._lowercase_keys": {"executed_lines": [179, 180, 181], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 166}, "_ODataBase._lowercase_list": {"executed_lines": [189, 190, 191], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 184}, "_ODataBase._extract_logical_table": {"executed_lines": [202, 203, 205, 206, 207, 208, 209, 210], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 194}, "_ODataBase._call_scope": {"executed_lines": [219, 220, 221, 222, 224], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 217}, "_ODataBase._format_key": {"executed_lines": [227, 228, 229, 231, 233, 237, 238, 239, 240, 241], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 226}, "_ODataBase._format_key.esc": {"executed_lines": [235], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 233}, "_ODataBase._build_alternate_key_str": {"executed_lines": [258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 270, 271], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 243}, "_ODataBase._label": {"executed_lines": [274, 275], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 273}, "_ODataBase._to_pascal": {"executed_lines": [287, 288], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 286}, "_ODataBase._normalize_picklist_label": {"executed_lines": [292, 295, 296, 298, 299], "summary": {"covered_lines": 5, "num_statements": 6, "percent_covered": 83.33333333333333, "percent_covered_display": "83", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 83.33333333333333, "percent_statements_covered_display": "83"}, "missing_lines": [293], "excluded_lines": [], "start_line": 290}, "_ODataBase._build_localizedlabels_payload": {"executed_lines": [310, 311, 312, 313, 314, 315, 316, 323, 324, 325], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 305}, "_ODataBase._enum_optionset_payload": {"executed_lines": [343, 344, 345, 348, 349, 350, 352, 353, 356, 358, 360, 361, 362, 364, 365, 366, 367, 368, 370, 371, 372, 373, 374, 375, 376, 377, 379, 380, 381, 382, 383, 384, 386, 387, 388, 389, 391, 393, 395, 396, 397, 398, 399, 400, 401, 409, 410], "summary": {"covered_lines": 47, "num_statements": 47, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 330}, "_ODataBase._attribute_payload": {"executed_lines": [427, 428, 429, 430, 433, 434, 435, 436, 445, 446, 455, 456, 465, 466, 475, 476, 485, 486, 494, 495, 513, 514, 520], "summary": {"covered_lines": 23, "num_statements": 23, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 423}, "_ODataBase._build_create_entity": {"executed_lines": [535, 536, 538, 539, 540, 541, 542, 543, 547, 548, 549, 550, 551, 552, 564, 565, 566, 567], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 526}, "_ODataBase._build_delete_entity": {"executed_lines": [575], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 573}, "_ODataBase._build_get_entity": {"executed_lines": [583, 584], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 581}, "_ODataBase._build_list_entities": {"executed_lines": [600, 601, 602, 604, 605, 606, 607, 608, 609, 610], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 593}, "_ODataBase._build_create_column": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 4, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [619, 620, 621, 625], "excluded_lines": [], "start_line": 612}, "_ODataBase._build_delete_column": {"executed_lines": [637], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 631}, "_ODataBase._build_lookup_field_models": {"executed_lines": [668, 669, 671, 683, 684, 687, 688, 695], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 644}, "_ODataBase._build_create_relationship": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 4, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [704, 705, 706, 707], "excluded_lines": [], "start_line": 697}, "_ODataBase._build_delete_relationship": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [716], "excluded_lines": [], "start_line": 714}, "_ODataBase._build_get_relationship": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [724, 725], "excluded_lines": [], "start_line": 722}, "_ODataBase._sql_guardrails": {"executed_lines": [800, 801, 802, 810, 811, 812, 820, 821, 829, 830, 837, 838, 845, 846, 858, 859, 869, 870, 880, 881, 891], "summary": {"covered_lines": 21, "num_statements": 21, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 766}, "_ODataBase.close": {"executed_lines": [902, 903, 904, 905], "summary": {"covered_lines": 4, "num_statements": 6, "percent_covered": 66.66666666666667, "percent_covered_display": "67", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 66.66666666666667, "percent_statements_covered_display": "67"}, "missing_lines": [906, 907], "excluded_lines": [], "start_line": 897}, "_ODataBase._flush_cache": {"executed_lines": [925, 926, 927, 932, 933, 934], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 913}, "": {"executed_lines": [10, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24, 26, 27, 33, 38, 39, 40, 42, 44, 45, 46, 47, 50, 78, 79, 82, 83, 84, 85, 86, 88, 89, 112, 120, 155, 156, 160, 161, 165, 166, 183, 184, 193, 194, 216, 217, 226, 243, 273, 286, 290, 305, 330, 423, 526, 573, 581, 593, 612, 631, 643, 644, 697, 714, 722, 735, 739, 740, 741, 746, 750, 751, 752, 753, 761, 766, 897, 913], "summary": {"covered_lines": 80, "num_statements": 80, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_RequestContext": {"executed_lines": [98, 99, 100, 101, 102, 103], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 79}, "_ODataBase": {"executed_lines": [130, 131, 133, 134, 140, 142, 143, 144, 145, 146, 158, 163, 179, 180, 181, 189, 190, 191, 202, 203, 205, 206, 207, 208, 209, 210, 219, 220, 221, 222, 224, 227, 228, 229, 231, 233, 235, 237, 238, 239, 240, 241, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 270, 271, 274, 275, 287, 288, 292, 295, 296, 298, 299, 310, 311, 312, 313, 314, 315, 316, 323, 324, 325, 343, 344, 345, 348, 349, 350, 352, 353, 356, 358, 360, 361, 362, 364, 365, 366, 367, 368, 370, 371, 372, 373, 374, 375, 376, 377, 379, 380, 381, 382, 383, 384, 386, 387, 388, 389, 391, 393, 395, 396, 397, 398, 399, 400, 401, 409, 410, 427, 428, 429, 430, 433, 434, 435, 436, 445, 446, 455, 456, 465, 466, 475, 476, 485, 486, 494, 495, 513, 514, 520, 535, 536, 538, 539, 540, 541, 542, 543, 547, 548, 549, 550, 551, 552, 564, 565, 566, 567, 575, 583, 584, 600, 601, 602, 604, 605, 606, 607, 608, 609, 610, 637, 668, 669, 671, 683, 684, 687, 688, 695, 800, 801, 802, 810, 811, 812, 820, 821, 829, 830, 837, 838, 845, 846, 858, 859, 869, 870, 880, 881, 891, 902, 903, 904, 905, 925, 926, 927, 932, 933, 934], "summary": {"covered_lines": 215, "num_statements": 232, "percent_covered": 92.67241379310344, "percent_covered_display": "93", "missing_lines": 17, "excluded_lines": 0, "percent_statements_covered": 92.67241379310344, "percent_statements_covered_display": "93"}, "missing_lines": [132, 147, 149, 293, 619, 620, 621, 625, 704, 705, 706, 707, 716, 724, 725, 906, 907], "excluded_lines": [], "start_line": 112}, "": {"executed_lines": [10, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24, 26, 27, 33, 38, 39, 40, 42, 44, 45, 46, 47, 50, 60, 61, 62, 63, 64, 70, 71, 72, 73, 74, 75, 78, 79, 82, 83, 84, 85, 86, 88, 89, 112, 120, 155, 156, 160, 161, 165, 166, 183, 184, 193, 194, 216, 217, 226, 243, 273, 286, 290, 305, 330, 423, 526, 573, 581, 593, 612, 631, 643, 644, 697, 714, 722, 735, 739, 740, 741, 746, 750, 751, 752, 753, 761, 766, 897, 913], "summary": {"covered_lines": 91, "num_statements": 91, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\data\\_raw_request.py": {"executed_lines": [6, 8, 9, 11, 14, 15, 29, 30, 31, 32, 33], "summary": {"covered_lines": 11, "num_statements": 11, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [6, 8, 9, 11, 14, 15, 29, 30, 31, 32, 33], "summary": {"covered_lines": 11, "num_statements": 11, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_RawRequest": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 15}, "": {"executed_lines": [6, 8, 9, 11, 14, 15, 29, 30, 31, 32, 33], "summary": {"covered_lines": 11, "num_statements": 11, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\data\\_relationships.py": {"executed_lines": [10, 12, 14, 15, 18, 28, 51, 54, 55, 57, 58, 59, 61, 64, 66, 74, 94, 96, 98, 99, 100, 102, 105, 107, 114, 123, 124, 125, 126, 128, 140, 141, 142, 143, 144, 145, 147, 171, 172, 173, 174, 175, 176, 177, 178, 180, 210, 211, 213, 214, 215, 220, 228, 229, 230, 231, 232, 233, 234, 236, 237, 238, 240, 241, 242, 244, 246, 256, 257, 258, 259], "summary": {"covered_lines": 71, "num_statements": 71, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"_RelationshipOperationsMixin._create_one_to_many_relationship": {"executed_lines": [51, 54, 55, 57, 58, 59, 61, 64, 66], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 28}, "_RelationshipOperationsMixin._create_many_to_many_relationship": {"executed_lines": [94, 96, 98, 99, 100, 102, 105, 107], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 74}, "_RelationshipOperationsMixin._delete_relationship": {"executed_lines": [123, 124, 125, 126], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 114}, "_RelationshipOperationsMixin._get_relationship": {"executed_lines": [140, 141, 142, 143, 144, 145], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 128}, "_RelationshipOperationsMixin._list_relationships": {"executed_lines": [171, 172, 173, 174, 175, 176, 177, 178], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 147}, "_RelationshipOperationsMixin._list_table_relationships": {"executed_lines": [210, 211, 213, 214, 215, 220, 228, 229, 230, 231, 232, 233, 234, 236, 237, 238, 240, 241, 242, 244], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 180}, "_RelationshipOperationsMixin._extract_id_from_header": {"executed_lines": [256, 257, 258, 259], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 246}, "": {"executed_lines": [10, 12, 14, 15, 18, 28, 74, 114, 128, 147, 180, 246], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_RelationshipOperationsMixin": {"executed_lines": [51, 54, 55, 57, 58, 59, 61, 64, 66, 94, 96, 98, 99, 100, 102, 105, 107, 123, 124, 125, 126, 140, 141, 142, 143, 144, 145, 171, 172, 173, 174, 175, 176, 177, 178, 210, 211, 213, 214, 215, 220, 228, 229, 230, 231, 232, 233, 234, 236, 237, 238, 240, 241, 242, 244, 256, 257, 258, 259], "summary": {"covered_lines": 59, "num_statements": 59, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 18}, "": {"executed_lines": [10, 12, 14, 15, 18, 28, 74, 114, 128, 147, 180, 246], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\data\\_upload.py": {"executed_lines": [6, 8, 11, 14, 43, 46, 49, 50, 51, 52, 53, 54, 56, 59, 61, 63, 64, 65, 66, 67, 70, 72, 73, 76, 77, 78, 80, 90, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 109, 110, 112, 114, 115, 117, 147, 148, 150, 151, 152, 153, 154, 155, 156, 157, 158, 161, 162, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 193, 194, 195], "summary": {"covered_lines": 87, "num_statements": 87, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"_FileUploadMixin._upload_file": {"executed_lines": [43, 46, 49, 50, 51, 52, 53, 54, 56, 59, 61, 63, 64, 65, 66, 67, 70, 72, 73, 76, 77, 78], "summary": {"covered_lines": 22, "num_statements": 22, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 14}, "_FileUploadMixin._upload_file_small": {"executed_lines": [90, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 109, 110, 112, 114, 115], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 80}, "_FileUploadMixin._upload_file_chunk": {"executed_lines": [147, 148, 150, 151, 152, 153, 154, 155, 156, 157, 158, 161, 162, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 193, 194, 195], "summary": {"covered_lines": 39, "num_statements": 39, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 117}, "": {"executed_lines": [6, 8, 11, 14, 80, 117], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_FileUploadMixin": {"executed_lines": [43, 46, 49, 50, 51, 52, 53, 54, 56, 59, 61, 63, 64, 65, 66, 67, 70, 72, 73, 76, 77, 78, 90, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 109, 110, 112, 114, 115, 147, 148, 150, 151, 152, 153, 154, 155, 156, 157, 158, 161, 162, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 193, 194, 195], "summary": {"covered_lines": 81, "num_statements": 81, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 11}, "": {"executed_lines": [6, 8, 11, 14, 80, 117], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\extensions\\__init__.py": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [9], "excluded_lines": [], "functions": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [9], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [9], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\models\\__init__.py": {"executed_lines": [19], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [19], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [19], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\models\\batch.py": {"executed_lines": [6, 8, 9, 11, 14, 15, 39, 40, 41, 42, 43, 44, 46, 47, 49, 52, 53, 71, 73, 74, 76, 78, 79, 81, 83, 84, 86, 88, 89, 106], "summary": {"covered_lines": 30, "num_statements": 30, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"BatchItemResponse.is_success": {"executed_lines": [49], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 47}, "BatchResult.succeeded": {"executed_lines": [76], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 74}, "BatchResult.failed": {"executed_lines": [81], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 79}, "BatchResult.has_errors": {"executed_lines": [86], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 84}, "BatchResult.entity_ids": {"executed_lines": [106], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 89}, "": {"executed_lines": [6, 8, 9, 11, 14, 15, 39, 40, 41, 42, 43, 44, 46, 47, 52, 53, 71, 73, 74, 78, 79, 83, 84, 88, 89], "summary": {"covered_lines": 25, "num_statements": 25, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"BatchItemResponse": {"executed_lines": [49], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 15}, "BatchResult": {"executed_lines": [76, 81, 86, 106], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 53}, "": {"executed_lines": [6, 8, 9, 11, 14, 15, 39, 40, 41, 42, 43, 44, 46, 47, 52, 53, 71, 73, 74, 78, 79, 83, 84, 88, 89], "summary": {"covered_lines": 25, "num_statements": 25, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\models\\filters.py": {"executed_lines": [34, 36, 37, 38, 39, 41, 67, 78, 79, 81, 82, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 95, 96, 97, 98, 99, 100, 101, 102, 103, 105, 113, 123, 125, 127, 128, 129, 130, 132, 133, 134, 135, 137, 138, 140, 141, 143, 144, 152, 155, 157, 158, 159, 160, 162, 163, 166, 169, 171, 172, 173, 174, 176, 177, 180, 183, 185, 186, 187, 189, 190, 193, 196, 198, 199, 200, 202, 203, 206, 209, 211, 212, 214, 215, 218, 221, 223, 224, 225, 226, 227, 229, 231, 232, 233, 236, 239, 241, 242, 243, 244, 245, 247, 249, 250, 251, 254, 257, 259, 260, 262, 263, 271, 282, 285, 292, 295, 302, 305, 312, 315, 322, 325, 332, 335, 342, 345, 352, 355, 362, 365, 380, 383, 389, 392, 398, 401, 416, 419, 434, 437, 452, 455, 465], "summary": {"covered_lines": 144, "num_statements": 144, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"_format_value": {"executed_lines": [78, 79, 81, 82, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 95, 96, 97, 98, 99, 100, 101, 102, 103, 105], "summary": {"covered_lines": 24, "num_statements": 24, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 67}, "FilterExpression.to_odata": {"executed_lines": [125], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 123}, "FilterExpression.__and__": {"executed_lines": [128, 129, 130], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 127}, "FilterExpression.__or__": {"executed_lines": [133, 134, 135], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 132}, "FilterExpression.__invert__": {"executed_lines": [138], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 137}, "FilterExpression.__str__": {"executed_lines": [141], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 140}, "FilterExpression.__repr__": {"executed_lines": [144], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 143}, "_ComparisonFilter.__init__": {"executed_lines": [158, 159, 160], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 157}, "_ComparisonFilter.to_odata": {"executed_lines": [163], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 162}, "_FunctionFilter.__init__": {"executed_lines": [172, 173, 174], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 171}, "_FunctionFilter.to_odata": {"executed_lines": [177], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 176}, "_AndFilter.__init__": {"executed_lines": [186, 187], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 185}, "_AndFilter.to_odata": {"executed_lines": [190], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 189}, "_OrFilter.__init__": {"executed_lines": [199, 200], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 198}, "_OrFilter.to_odata": {"executed_lines": [203], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 202}, "_NotFilter.__init__": {"executed_lines": [212], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 211}, "_NotFilter.to_odata": {"executed_lines": [215], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 214}, "_InFilter.__init__": {"executed_lines": [224, 225, 226, 227], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 223}, "_InFilter.to_odata": {"executed_lines": [231, 232, 233], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 229}, "_NotInFilter.__init__": {"executed_lines": [242, 243, 244, 245], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 241}, "_NotInFilter.to_odata": {"executed_lines": [249, 250, 251], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 247}, "_RawFilter.__init__": {"executed_lines": [260], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 259}, "_RawFilter.to_odata": {"executed_lines": [263], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 262}, "eq": {"executed_lines": [282], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 271}, "ne": {"executed_lines": [292], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 285}, "gt": {"executed_lines": [302], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 295}, "ge": {"executed_lines": [312], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 305}, "lt": {"executed_lines": [322], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 315}, "le": {"executed_lines": [332], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 325}, "contains": {"executed_lines": [342], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 335}, "startswith": {"executed_lines": [352], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 345}, "endswith": {"executed_lines": [362], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 355}, "between": {"executed_lines": [380], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 365}, "is_null": {"executed_lines": [389], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 383}, "is_not_null": {"executed_lines": [398], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 392}, "filter_in": {"executed_lines": [416], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 401}, "not_in": {"executed_lines": [434], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 419}, "not_between": {"executed_lines": [452], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 437}, "raw": {"executed_lines": [465], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 455}, "": {"executed_lines": [34, 36, 37, 38, 39, 41, 67, 113, 123, 127, 132, 137, 140, 143, 152, 155, 157, 162, 166, 169, 171, 176, 180, 183, 185, 189, 193, 196, 198, 202, 206, 209, 211, 214, 218, 221, 223, 229, 236, 239, 241, 247, 254, 257, 259, 262, 271, 285, 295, 305, 315, 325, 335, 345, 355, 365, 383, 392, 401, 419, 437, 455], "summary": {"covered_lines": 62, "num_statements": 62, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"FilterExpression": {"executed_lines": [125, 128, 129, 130, 133, 134, 135, 138, 141, 144], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 113}, "_ComparisonFilter": {"executed_lines": [158, 159, 160, 163], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 152}, "_FunctionFilter": {"executed_lines": [172, 173, 174, 177], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 166}, "_AndFilter": {"executed_lines": [186, 187, 190], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 180}, "_OrFilter": {"executed_lines": [199, 200, 203], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 193}, "_NotFilter": {"executed_lines": [212, 215], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 206}, "_InFilter": {"executed_lines": [224, 225, 226, 227, 231, 232, 233], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 218}, "_NotInFilter": {"executed_lines": [242, 243, 244, 245, 249, 250, 251], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 236}, "_RawFilter": {"executed_lines": [260, 263], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 254}, "": {"executed_lines": [34, 36, 37, 38, 39, 41, 67, 78, 79, 81, 82, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 95, 96, 97, 98, 99, 100, 101, 102, 103, 105, 113, 123, 127, 132, 137, 140, 143, 152, 155, 157, 162, 166, 169, 171, 176, 180, 183, 185, 189, 193, 196, 198, 202, 206, 209, 211, 214, 218, 221, 223, 229, 236, 239, 241, 247, 254, 257, 259, 262, 271, 282, 285, 292, 295, 302, 305, 312, 315, 322, 325, 332, 335, 342, 345, 352, 355, 362, 365, 380, 383, 389, 392, 398, 401, 416, 419, 434, 437, 452, 455, 465], "summary": {"covered_lines": 102, "num_statements": 102, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\models\\labels.py": {"executed_lines": [6, 8, 9, 11, 17, 18, 31, 32, 33, 35, 49, 54, 55, 56, 59, 60, 73, 74, 75, 77, 93, 98, 99, 100, 101, 102, 103, 104, 107], "summary": {"covered_lines": 29, "num_statements": 29, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"LocalizedLabel.to_dict": {"executed_lines": [49, 54, 55, 56], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 35}, "Label.to_dict": {"executed_lines": [93, 98, 99, 100, 101, 102, 103, 104], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 77}, "": {"executed_lines": [6, 8, 9, 11, 17, 18, 31, 32, 33, 35, 59, 60, 73, 74, 75, 77, 107], "summary": {"covered_lines": 17, "num_statements": 17, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"LocalizedLabel": {"executed_lines": [49, 54, 55, 56], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 18}, "Label": {"executed_lines": [93, 98, 99, 100, 101, 102, 103, 104], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 60}, "": {"executed_lines": [6, 8, 9, 11, 17, 18, 31, 32, 33, 35, 59, 60, 73, 74, 75, 77, 107], "summary": {"covered_lines": 17, "num_statements": 17, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\models\\query_builder.py": {"executed_lines": [47, 49, 51, 53, 54, 56, 59, 66, 67, 68, 69, 70, 71, 72, 73, 74, 77, 102, 103, 104, 105, 106, 107, 109, 115, 116, 118, 124, 125, 127, 134, 135, 136, 138, 144, 145, 147, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 167, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 207, 220, 221, 225, 232, 233, 235, 242, 243, 245, 252, 253, 255, 262, 263, 265, 272, 273, 275, 282, 283, 287, 294, 295, 297, 304, 305, 307, 314, 315, 319, 325, 326, 328, 334, 335, 339, 353, 354, 356, 370, 371, 373, 386, 387, 389, 402, 403, 405, 425, 426, 430, 453, 454, 455, 456, 460, 469, 470, 471, 475, 482, 483, 484, 485, 487, 497, 498, 499, 500, 502, 518, 519, 521, 548, 549, 551, 574, 575, 579, 602, 603, 604, 606, 607, 611, 623, 624, 625, 626, 627, 628, 629, 630, 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645, 646, 650, 659, 660, 667, 713, 714, 718, 719, 720, 722, 734, 735, 737, 738, 739, 741, 745, 775, 776, 780, 781, 782], "summary": {"covered_lines": 195, "num_statements": 195, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"ExpandOption.__init__": {"executed_lines": [103, 104, 105, 106, 107], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 102}, "ExpandOption.select": {"executed_lines": [115, 116], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 109}, "ExpandOption.filter": {"executed_lines": [124, 125], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 118}, "ExpandOption.order_by": {"executed_lines": [134, 135, 136], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 127}, "ExpandOption.top": {"executed_lines": [144, 145], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 138}, "ExpandOption.to_odata": {"executed_lines": [153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 147}, "QueryBuilder.__init__": {"executed_lines": [191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 190}, "QueryBuilder.select": {"executed_lines": [220, 221], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 207}, "QueryBuilder.filter_eq": {"executed_lines": [232, 233], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 225}, "QueryBuilder.filter_ne": {"executed_lines": [242, 243], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 235}, "QueryBuilder.filter_gt": {"executed_lines": [252, 253], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 245}, "QueryBuilder.filter_ge": {"executed_lines": [262, 263], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 255}, "QueryBuilder.filter_lt": {"executed_lines": [272, 273], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 265}, "QueryBuilder.filter_le": {"executed_lines": [282, 283], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 275}, "QueryBuilder.filter_contains": {"executed_lines": [294, 295], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 287}, "QueryBuilder.filter_startswith": {"executed_lines": [304, 305], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 297}, "QueryBuilder.filter_endswith": {"executed_lines": [314, 315], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 307}, "QueryBuilder.filter_null": {"executed_lines": [325, 326], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 319}, "QueryBuilder.filter_not_null": {"executed_lines": [334, 335], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 328}, "QueryBuilder.filter_in": {"executed_lines": [353, 354], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 339}, "QueryBuilder.filter_not_in": {"executed_lines": [370, 371], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 356}, "QueryBuilder.filter_between": {"executed_lines": [386, 387], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 373}, "QueryBuilder.filter_not_between": {"executed_lines": [402, 403], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 389}, "QueryBuilder.filter_raw": {"executed_lines": [425, 426], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 405}, "QueryBuilder.where": {"executed_lines": [453, 454, 455, 456], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 430}, "QueryBuilder.order_by": {"executed_lines": [469, 470, 471], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 460}, "QueryBuilder.top": {"executed_lines": [482, 483, 484, 485], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 475}, "QueryBuilder.page_size": {"executed_lines": [497, 498, 499, 500], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 487}, "QueryBuilder.count": {"executed_lines": [518, 519], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 502}, "QueryBuilder.include_formatted_values": {"executed_lines": [548, 549], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 521}, "QueryBuilder.include_annotations": {"executed_lines": [574, 575], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 551}, "QueryBuilder.expand": {"executed_lines": [602, 603, 604, 606, 607], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 579}, "QueryBuilder.build": {"executed_lines": [623, 624, 625, 626, 627, 628, 629, 630, 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645, 646], "summary": {"covered_lines": 23, "num_statements": 23, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 611}, "QueryBuilder._validate_constraints": {"executed_lines": [659, 660], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 650}, "QueryBuilder.execute": {"executed_lines": [713, 714, 718, 719, 720, 722, 734, 735, 737, 741], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 667}, "QueryBuilder.execute._flat": {"executed_lines": [738, 739], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 737}, "QueryBuilder.to_dataframe": {"executed_lines": [775, 776, 780, 781, 782], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 745}, "": {"executed_lines": [47, 49, 51, 53, 54, 56, 59, 66, 67, 68, 69, 70, 71, 72, 73, 74, 77, 102, 109, 118, 127, 138, 147, 167, 190, 207, 225, 235, 245, 255, 265, 275, 287, 297, 307, 319, 328, 339, 356, 373, 389, 405, 430, 460, 475, 487, 502, 521, 551, 579, 611, 650, 667, 745], "summary": {"covered_lines": 54, "num_statements": 54, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"QueryParams": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 59}, "ExpandOption": {"executed_lines": [103, 104, 105, 106, 107, 115, 116, 124, 125, 134, 135, 136, 144, 145, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164], "summary": {"covered_lines": 26, "num_statements": 26, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 77}, "QueryBuilder": {"executed_lines": [191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 220, 221, 232, 233, 242, 243, 252, 253, 262, 263, 272, 273, 282, 283, 294, 295, 304, 305, 314, 315, 325, 326, 334, 335, 353, 354, 370, 371, 386, 387, 402, 403, 425, 426, 453, 454, 455, 456, 469, 470, 471, 482, 483, 484, 485, 497, 498, 499, 500, 518, 519, 548, 549, 574, 575, 602, 603, 604, 606, 607, 623, 624, 625, 626, 627, 628, 629, 630, 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645, 646, 659, 660, 713, 714, 718, 719, 720, 722, 734, 735, 737, 738, 739, 741, 775, 776, 780, 781, 782], "summary": {"covered_lines": 115, "num_statements": 115, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 167}, "": {"executed_lines": [47, 49, 51, 53, 54, 56, 59, 66, 67, 68, 69, 70, 71, 72, 73, 74, 77, 102, 109, 118, 127, 138, 147, 167, 190, 207, 225, 235, 245, 255, 265, 275, 287, 297, 307, 319, 328, 339, 356, 373, 389, 405, 430, 460, 475, 487, 502, 521, 551, 579, 611, 650, 667, 745], "summary": {"covered_lines": 54, "num_statements": 54, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\models\\record.py": {"executed_lines": [6, 8, 9, 11, 13, 16, 17, 41, 42, 43, 44, 48, 49, 51, 52, 54, 55, 57, 58, 60, 61, 63, 64, 66, 68, 70, 72, 74, 76, 78, 80, 84, 85, 106, 107, 108, 112, 114], "summary": {"covered_lines": 38, "num_statements": 38, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"Record.__getitem__": {"executed_lines": [49], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 48}, "Record.__setitem__": {"executed_lines": [52], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 51}, "Record.__delitem__": {"executed_lines": [55], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 54}, "Record.__contains__": {"executed_lines": [58], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 57}, "Record.__iter__": {"executed_lines": [61], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 60}, "Record.__len__": {"executed_lines": [64], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 63}, "Record.get": {"executed_lines": [68], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 66}, "Record.keys": {"executed_lines": [72], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 70}, "Record.values": {"executed_lines": [76], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 74}, "Record.items": {"executed_lines": [80], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 78}, "Record.from_api_response": {"executed_lines": [106, 107, 108], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 85}, "Record.to_dict": {"executed_lines": [114], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 112}, "": {"executed_lines": [6, 8, 9, 11, 13, 16, 17, 41, 42, 43, 44, 48, 51, 54, 57, 60, 63, 66, 70, 74, 78, 84, 85, 112], "summary": {"covered_lines": 24, "num_statements": 24, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"Record": {"executed_lines": [49, 52, 55, 58, 61, 64, 68, 72, 76, 80, 106, 107, 108, 114], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 17}, "": {"executed_lines": [6, 8, 9, 11, 13, 16, 17, 41, 42, 43, 44, 48, 51, 54, 57, 60, 63, 66, 70, 74, 78, 84, 85, 112], "summary": {"covered_lines": 24, "num_statements": 24, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\models\\relationship.py": {"executed_lines": [6, 8, 9, 11, 20, 23, 24, 52, 53, 54, 55, 56, 57, 58, 60, 77, 85, 86, 87, 90, 91, 116, 117, 118, 119, 120, 122, 142, 154, 155, 156, 157, 158, 161, 162, 185, 186, 187, 188, 189, 190, 191, 193, 215, 223, 224, 225, 226, 227, 230, 231, 251, 252, 253, 254, 255, 257, 278, 279, 286, 287, 288, 291, 292, 324, 325, 326, 329, 330, 331, 334, 335, 337, 338, 361, 370, 371, 391, 399, 400, 414, 415, 416, 418, 419, 429, 430, 437, 440], "summary": {"covered_lines": 89, "num_statements": 89, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"CascadeConfiguration.to_dict": {"executed_lines": [77, 85, 86, 87], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 60}, "LookupAttributeMetadata.to_dict": {"executed_lines": [142, 154, 155, 156, 157, 158], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 122}, "OneToManyRelationshipMetadata.to_dict": {"executed_lines": [215, 223, 224, 225, 226, 227], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 193}, "ManyToManyRelationshipMetadata.to_dict": {"executed_lines": [278, 279, 286, 287, 288], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 257}, "RelationshipInfo.from_one_to_many": {"executed_lines": [361], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 338}, "RelationshipInfo.from_many_to_many": {"executed_lines": [391], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 371}, "RelationshipInfo.from_api_response": {"executed_lines": [414, 415, 416, 418, 419, 429, 430, 437], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 400}, "": {"executed_lines": [6, 8, 9, 11, 20, 23, 24, 52, 53, 54, 55, 56, 57, 58, 60, 90, 91, 116, 117, 118, 119, 120, 122, 161, 162, 185, 186, 187, 188, 189, 190, 191, 193, 230, 231, 251, 252, 253, 254, 255, 257, 291, 292, 324, 325, 326, 329, 330, 331, 334, 335, 337, 338, 370, 371, 399, 400, 440], "summary": {"covered_lines": 58, "num_statements": 58, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"CascadeConfiguration": {"executed_lines": [77, 85, 86, 87], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 24}, "LookupAttributeMetadata": {"executed_lines": [142, 154, 155, 156, 157, 158], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 91}, "OneToManyRelationshipMetadata": {"executed_lines": [215, 223, 224, 225, 226, 227], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 162}, "ManyToManyRelationshipMetadata": {"executed_lines": [278, 279, 286, 287, 288], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 231}, "RelationshipInfo": {"executed_lines": [361, 391, 414, 415, 416, 418, 419, 429, 430, 437], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 292}, "": {"executed_lines": [6, 8, 9, 11, 20, 23, 24, 52, 53, 54, 55, 56, 57, 58, 60, 90, 91, 116, 117, 118, 119, 120, 122, 161, 162, 185, 186, 187, 188, 189, 190, 191, 193, 230, 231, 251, 252, 253, 254, 255, 257, 291, 292, 324, 325, 326, 329, 330, 331, 334, 335, 337, 338, 370, 371, 399, 400, 440], "summary": {"covered_lines": 58, "num_statements": 58, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\models\\table_info.py": {"executed_lines": [6, 8, 9, 11, 14, 15, 36, 37, 38, 39, 40, 41, 42, 43, 45, 46, 54, 55, 56, 59, 60, 61, 64, 65, 67, 79, 80, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 123, 135, 137, 139, 140, 141, 142, 143, 145, 146, 147, 148, 149, 151, 152, 154, 155, 157, 159, 160, 161, 162, 164, 166, 168, 170, 172, 174, 178, 179, 189, 199, 200, 208, 209, 210, 213, 214, 215, 217, 230, 232, 235, 236, 249, 250, 251, 252, 254, 255, 262], "summary": {"covered_lines": 88, "num_statements": 88, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"ColumnInfo.from_api_response": {"executed_lines": [54, 55, 56, 59, 60, 61, 64, 65, 67], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 46}, "TableInfo._resolve_key": {"executed_lines": [137], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 135}, "TableInfo.__getitem__": {"executed_lines": [140, 141, 142, 143], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 139}, "TableInfo.__contains__": {"executed_lines": [146, 147, 148, 149], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 145}, "TableInfo.__iter__": {"executed_lines": [152], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 151}, "TableInfo.__len__": {"executed_lines": [155], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 154}, "TableInfo.get": {"executed_lines": [159, 160, 161, 162], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 157}, "TableInfo.keys": {"executed_lines": [166], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 164}, "TableInfo.values": {"executed_lines": [170], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 168}, "TableInfo.items": {"executed_lines": [174], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 172}, "TableInfo.from_dict": {"executed_lines": [189], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 179}, "TableInfo.from_api_response": {"executed_lines": [208, 209, 210, 213, 214, 215, 217], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 200}, "TableInfo.to_dict": {"executed_lines": [232], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 230}, "AlternateKeyInfo.from_api_response": {"executed_lines": [262], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 255}, "": {"executed_lines": [6, 8, 9, 11, 14, 15, 36, 37, 38, 39, 40, 41, 42, 43, 45, 46, 79, 80, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 123, 135, 139, 145, 151, 154, 157, 164, 168, 172, 178, 179, 199, 200, 230, 235, 236, 249, 250, 251, 252, 254, 255], "summary": {"covered_lines": 51, "num_statements": 51, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"ColumnInfo": {"executed_lines": [54, 55, 56, 59, 60, 61, 64, 65, 67], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 15}, "TableInfo": {"executed_lines": [137, 140, 141, 142, 143, 146, 147, 148, 149, 152, 155, 159, 160, 161, 162, 166, 170, 174, 189, 208, 209, 210, 213, 214, 215, 217, 232], "summary": {"covered_lines": 27, "num_statements": 27, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 80}, "AlternateKeyInfo": {"executed_lines": [262], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 236}, "": {"executed_lines": [6, 8, 9, 11, 14, 15, 36, 37, 38, 39, 40, 41, 42, 43, 45, 46, 79, 80, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 123, 135, 139, 145, 151, 154, 157, 164, 168, 172, 178, 179, 199, 200, 230, 235, 236, 249, 250, 251, 252, 254, 255], "summary": {"covered_lines": 51, "num_statements": 51, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\models\\upsert.py": {"executed_lines": [6, 8, 9, 11, 14, 15, 38, 39], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [6, 8, 9, 11, 14, 15, 38, 39], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"UpsertItem": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 15}, "": {"executed_lines": [6, 8, 9, 11, 14, 15, 38, 39], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\operations\\__init__.py": {"executed_lines": [11, 13], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [11, 13], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [11, 13], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\operations\\batch.py": {"executed_lines": [6, 8, 10, 12, 13, 14, 35, 36, 37, 42, 47, 64, 75, 76, 78, 101, 103, 116, 118, 128, 131, 149, 150, 151, 153, 154, 156, 157, 165, 177, 178, 180, 197, 199, 219, 221, 244, 246, 274, 276, 316, 317, 318, 319, 320, 321, 322, 323, 325, 326, 329, 351, 352, 354, 383, 393, 402, 404, 413, 415, 436, 438, 450, 452, 465, 467, 484, 486, 500, 502, 509, 511, 520, 522, 558, 578, 589, 590, 592, 610, 611, 612, 620, 643, 644, 646, 665, 666, 667, 668, 670, 672, 673, 674, 675, 679, 681, 716, 717, 718, 719, 720, 721, 723, 724, 725, 726, 730, 732, 733, 738, 740, 741, 742, 743, 744, 745, 747, 749, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782, 790, 820, 821, 822, 823, 824, 825, 826, 827, 829, 844, 845, 846, 848, 866, 867, 870, 886, 887, 889, 895], "summary": {"covered_lines": 150, "num_statements": 151, "percent_covered": 99.33774834437087, "percent_covered_display": "99", "missing_lines": 1, "excluded_lines": 2, "percent_statements_covered": 99.33774834437087, "percent_statements_covered_display": "99"}, "missing_lines": [734], "excluded_lines": [44, 45], "functions": {"ChangeSetRecordOperations.__init__": {"executed_lines": [76], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 75}, "ChangeSetRecordOperations.create": {"executed_lines": [101], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 78}, "ChangeSetRecordOperations.update": {"executed_lines": [116], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 103}, "ChangeSetRecordOperations.delete": {"executed_lines": [128], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 118}, "ChangeSet.__init__": {"executed_lines": [150, 151], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 149}, "ChangeSet.__enter__": {"executed_lines": [154], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 153}, "ChangeSet.__exit__": {"executed_lines": [157], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 156}, "BatchRecordOperations.__init__": {"executed_lines": [178], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 177}, "BatchRecordOperations.create": {"executed_lines": [197], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 180}, "BatchRecordOperations.update": {"executed_lines": [219], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 199}, "BatchRecordOperations.delete": {"executed_lines": [244], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 221}, "BatchRecordOperations.get": {"executed_lines": [274], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 246}, "BatchRecordOperations.upsert": {"executed_lines": [316, 317, 318, 319, 320, 321, 322, 323, 325, 326], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 276}, "BatchTableOperations.__init__": {"executed_lines": [352], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 351}, "BatchTableOperations.create": {"executed_lines": [383], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 354}, "BatchTableOperations.delete": {"executed_lines": [402], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 393}, "BatchTableOperations.get": {"executed_lines": [413], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 404}, "BatchTableOperations.list": {"executed_lines": [436], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 415}, "BatchTableOperations.add_columns": {"executed_lines": [450], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 438}, "BatchTableOperations.remove_columns": {"executed_lines": [465], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 452}, "BatchTableOperations.create_one_to_many_relationship": {"executed_lines": [484], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 467}, "BatchTableOperations.create_many_to_many_relationship": {"executed_lines": [500], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 486}, "BatchTableOperations.delete_relationship": {"executed_lines": [509], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 502}, "BatchTableOperations.get_relationship": {"executed_lines": [520], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 511}, "BatchTableOperations.create_lookup_field": {"executed_lines": [558], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 522}, "BatchQueryOperations.__init__": {"executed_lines": [590], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 589}, "BatchQueryOperations.sql": {"executed_lines": [610, 611, 612], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 592}, "BatchDataFrameOperations.__init__": {"executed_lines": [644], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 643}, "BatchDataFrameOperations.create": {"executed_lines": [665, 666, 667, 668, 670, 672, 673, 674, 675, 679], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 646}, "BatchDataFrameOperations.update": {"executed_lines": [716, 717, 718, 719, 720, 721, 723, 724, 725, 726, 730, 732, 733, 738, 740, 741, 742, 743, 744, 745, 747], "summary": {"covered_lines": 21, "num_statements": 22, "percent_covered": 95.45454545454545, "percent_covered_display": "95", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 95.45454545454545, "percent_statements_covered_display": "95"}, "missing_lines": [734], "excluded_lines": [], "start_line": 681}, "BatchDataFrameOperations.delete": {"executed_lines": [773, 774, 775, 776, 777, 778, 779, 780, 781, 782], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 749}, "BatchRequest.__init__": {"executed_lines": [821, 822, 823, 824, 825, 826, 827], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 820}, "BatchRequest.changeset": {"executed_lines": [844, 845, 846], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 829}, "BatchRequest.execute": {"executed_lines": [866, 867], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 848}, "BatchOperations.__init__": {"executed_lines": [887], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 886}, "BatchOperations.new": {"executed_lines": [895], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 889}, "": {"executed_lines": [6, 8, 10, 12, 13, 14, 35, 36, 37, 42, 47, 64, 75, 78, 103, 118, 131, 149, 153, 156, 165, 177, 180, 199, 221, 246, 276, 329, 351, 354, 393, 404, 415, 438, 452, 467, 486, 502, 511, 522, 578, 589, 592, 620, 643, 646, 681, 749, 790, 820, 829, 848, 870, 886, 889], "summary": {"covered_lines": 55, "num_statements": 55, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [44, 45], "start_line": 1}}, "classes": {"ChangeSetRecordOperations": {"executed_lines": [76, 101, 116, 128], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 64}, "ChangeSet": {"executed_lines": [150, 151, 154, 157], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 131}, "BatchRecordOperations": {"executed_lines": [178, 197, 219, 244, 274, 316, 317, 318, 319, 320, 321, 322, 323, 325, 326], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 165}, "BatchTableOperations": {"executed_lines": [352, 383, 402, 413, 436, 450, 465, 484, 500, 509, 520, 558], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 329}, "BatchQueryOperations": {"executed_lines": [590, 610, 611, 612], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 578}, "BatchDataFrameOperations": {"executed_lines": [644, 665, 666, 667, 668, 670, 672, 673, 674, 675, 679, 716, 717, 718, 719, 720, 721, 723, 724, 725, 726, 730, 732, 733, 738, 740, 741, 742, 743, 744, 745, 747, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782], "summary": {"covered_lines": 42, "num_statements": 43, "percent_covered": 97.67441860465117, "percent_covered_display": "98", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 97.67441860465117, "percent_statements_covered_display": "98"}, "missing_lines": [734], "excluded_lines": [], "start_line": 620}, "BatchRequest": {"executed_lines": [821, 822, 823, 824, 825, 826, 827, 844, 845, 846, 866, 867], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 790}, "BatchOperations": {"executed_lines": [887, 895], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 870}, "": {"executed_lines": [6, 8, 10, 12, 13, 14, 35, 36, 37, 42, 47, 64, 75, 78, 103, 118, 131, 149, 153, 156, 165, 177, 180, 199, 221, 246, 276, 329, 351, 354, 393, 404, 415, 438, 452, 467, 486, 502, 511, 522, 578, 589, 592, 620, 643, 646, 681, 749, 790, 820, 829, 848, 870, 886, 889], "summary": {"covered_lines": 55, "num_statements": 55, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [44, 45], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\operations\\dataframe.py": {"executed_lines": [6, 8, 10, 12, 18, 21, 51, 52, 56, 91, 92, 93, 94, 98, 167, 168, 169, 170, 171, 172, 176, 181, 183, 184, 195, 197, 198, 199, 203, 238, 239, 241, 242, 244, 247, 248, 249, 254, 256, 257, 259, 263, 324, 325, 326, 327, 328, 329, 331, 332, 333, 334, 338, 340, 341, 342, 345, 348, 349, 350, 351, 352, 354, 355, 357, 361, 394, 395, 397, 398, 399, 401, 402, 403, 406, 408, 409, 410, 411], "summary": {"covered_lines": 79, "num_statements": 79, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [14, 15], "functions": {"DataFrameOperations.__init__": {"executed_lines": [52], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 51}, "DataFrameOperations.sql": {"executed_lines": [91, 92, 93, 94], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 56}, "DataFrameOperations.get": {"executed_lines": [167, 168, 169, 170, 171, 172, 176, 181, 183, 184, 195, 197, 198, 199], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 98}, "DataFrameOperations.create": {"executed_lines": [238, 239, 241, 242, 244, 247, 248, 249, 254, 256, 257, 259], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 203}, "DataFrameOperations.update": {"executed_lines": [324, 325, 326, 327, 328, 329, 331, 332, 333, 334, 338, 340, 341, 342, 345, 348, 349, 350, 351, 352, 354, 355, 357], "summary": {"covered_lines": 23, "num_statements": 23, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 263}, "DataFrameOperations.delete": {"executed_lines": [394, 395, 397, 398, 399, 401, 402, 403, 406, 408, 409, 410, 411], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 361}, "": {"executed_lines": [6, 8, 10, 12, 18, 21, 51, 56, 98, 203, 263, 361], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [14, 15], "start_line": 1}}, "classes": {"DataFrameOperations": {"executed_lines": [52, 91, 92, 93, 94, 167, 168, 169, 170, 171, 172, 176, 181, 183, 184, 195, 197, 198, 199, 238, 239, 241, 242, 244, 247, 248, 249, 254, 256, 257, 259, 324, 325, 326, 327, 328, 329, 331, 332, 333, 334, 338, 340, 341, 342, 345, 348, 349, 350, 351, 352, 354, 355, 357, 394, 395, 397, 398, 399, 401, 402, 403, 406, 408, 409, 410, 411], "summary": {"covered_lines": 67, "num_statements": 67, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 21}, "": {"executed_lines": [6, 8, 10, 12, 18, 21, 51, 56, 98, 203, 263, 361], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [14, 15], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\operations\\files.py": {"executed_lines": [6, 8, 14, 17, 35, 36, 40, 104, 105], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [10, 11], "functions": {"FileOperations.__init__": {"executed_lines": [36], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 35}, "FileOperations.upload": {"executed_lines": [104, 105], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 40}, "": {"executed_lines": [6, 8, 14, 17, 35, 40], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [10, 11], "start_line": 1}}, "classes": {"FileOperations": {"executed_lines": [36, 104, 105], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 17}, "": {"executed_lines": [6, 8, 14, 17, 35, 40], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [10, 11], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\operations\\query.py": {"executed_lines": [6, 8, 10, 11, 12, 18, 21, 49, 50, 54, 89, 90, 91, 95, 145, 146, 147, 151, 181, 190, 203, 204, 205, 206, 208, 209, 212, 213, 215, 216, 217, 218, 219, 220, 221, 230, 231, 235, 260, 261, 265, 309, 310, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 330, 331, 332, 333, 334, 335, 336, 337, 339, 349, 350, 354, 391, 392, 393, 394, 395, 400, 401, 402, 403, 411, 436, 437, 441, 478, 479, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 494, 495, 496, 497, 498, 499, 501, 511, 512, 516, 548, 549, 550, 551, 552, 557, 561, 597, 598, 599, 600, 601, 606, 607, 608, 609], "summary": {"covered_lines": 117, "num_statements": 120, "percent_covered": 97.5, "percent_covered_display": "98", "missing_lines": 3, "excluded_lines": 3, "percent_statements_covered": 97.5, "percent_statements_covered_display": "98"}, "missing_lines": [207, 323, 491], "excluded_lines": [14, 15, 445], "functions": {"QueryOperations.__init__": {"executed_lines": [50], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 49}, "QueryOperations.builder": {"executed_lines": [89, 90, 91], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 54}, "QueryOperations.sql": {"executed_lines": [145, 146, 147], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 95}, "QueryOperations.sql_columns": {"executed_lines": [181, 190, 203, 204, 205, 206, 208, 209, 212, 213, 215, 216, 217, 218, 219, 220, 221, 230, 231], "summary": {"covered_lines": 19, "num_statements": 20, "percent_covered": 95.0, "percent_covered_display": "95", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 95.0, "percent_statements_covered_display": "95"}, "missing_lines": [207], "excluded_lines": [], "start_line": 151}, "QueryOperations.sql_select": {"executed_lines": [260, 261], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 235}, "QueryOperations.sql_joins": {"executed_lines": [309, 310, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 330, 331, 332, 333, 334, 335, 336, 337, 339, 349, 350], "summary": {"covered_lines": 24, "num_statements": 25, "percent_covered": 96.0, "percent_covered_display": "96", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 96.0, "percent_statements_covered_display": "96"}, "missing_lines": [323], "excluded_lines": [], "start_line": 265}, "QueryOperations.sql_join": {"executed_lines": [391, 392, 393, 394, 395, 400, 401, 402, 403], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 354}, "QueryOperations.odata_select": {"executed_lines": [436, 437], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 411}, "QueryOperations.odata_expands": {"executed_lines": [478, 479, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 494, 495, 496, 497, 498, 499, 501, 511, 512], "summary": {"covered_lines": 21, "num_statements": 22, "percent_covered": 95.45454545454545, "percent_covered_display": "95", "missing_lines": 1, "excluded_lines": 1, "percent_statements_covered": 95.45454545454545, "percent_statements_covered_display": "95"}, "missing_lines": [491], "excluded_lines": [445], "start_line": 441}, "QueryOperations.odata_expand": {"executed_lines": [548, 549, 550, 551, 552, 557], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 516}, "QueryOperations.odata_bind": {"executed_lines": [597, 598, 599, 600, 601, 606, 607, 608, 609], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 561}, "": {"executed_lines": [6, 8, 10, 11, 12, 18, 21, 49, 54, 95, 151, 235, 265, 354, 411, 441, 516, 561], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [14, 15], "start_line": 1}}, "classes": {"QueryOperations": {"executed_lines": [50, 89, 90, 91, 145, 146, 147, 181, 190, 203, 204, 205, 206, 208, 209, 212, 213, 215, 216, 217, 218, 219, 220, 221, 230, 231, 260, 261, 309, 310, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 330, 331, 332, 333, 334, 335, 336, 337, 339, 349, 350, 391, 392, 393, 394, 395, 400, 401, 402, 403, 436, 437, 478, 479, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 494, 495, 496, 497, 498, 499, 501, 511, 512, 548, 549, 550, 551, 552, 557, 597, 598, 599, 600, 601, 606, 607, 608, 609], "summary": {"covered_lines": 99, "num_statements": 102, "percent_covered": 97.05882352941177, "percent_covered_display": "97", "missing_lines": 3, "excluded_lines": 1, "percent_statements_covered": 97.05882352941177, "percent_statements_covered_display": "97"}, "missing_lines": [207, 323, 491], "excluded_lines": [445], "start_line": 21}, "": {"executed_lines": [6, 8, 10, 11, 12, 18, 21, 49, 54, 95, 151, 235, 265, 354, 411, 441, 516, 561], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [14, 15], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\operations\\records.py": {"executed_lines": [6, 8, 10, 11, 17, 20, 46, 47, 57, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 110, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 174, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 228, 229, 262, 263, 331, 426, 427, 428, 429, 438, 443, 444, 445, 447, 448, 449, 460, 462, 466, 542, 543, 544, 545, 546, 547, 548, 549, 551, 552, 553, 554, 555, 556, 558, 559, 560, 561], "summary": {"covered_lines": 86, "num_statements": 86, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 18, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [13, 14, 51, 52, 53, 54, 55, 56, 168, 169, 170, 171, 172, 173, 260, 261, 329, 330], "functions": {"RecordOperations.__init__": {"executed_lines": [47], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 46}, "RecordOperations.create": {"executed_lines": [94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 57}, "RecordOperations.update": {"executed_lines": [155, 156, 157, 158, 159, 160, 161, 162, 163, 164], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 110}, "RecordOperations.delete": {"executed_lines": [210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 174}, "RecordOperations.get": {"executed_lines": [426, 427, 428, 429, 438, 443, 444, 445, 447, 462], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 331}, "RecordOperations.get._paged": {"executed_lines": [448, 449, 460], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 447}, "RecordOperations.upsert": {"executed_lines": [542, 543, 544, 545, 546, 547, 548, 549, 551, 552, 553, 554, 555, 556, 558, 559, 560, 561], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 466}, "": {"executed_lines": [6, 8, 10, 11, 17, 20, 46, 57, 110, 174, 228, 229, 262, 263, 331, 466], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 12, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [13, 14, 51, 53, 54, 56, 168, 170, 171, 173, 261, 330], "start_line": 1}}, "classes": {"RecordOperations": {"executed_lines": [47, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 426, 427, 428, 429, 438, 443, 444, 445, 447, 448, 449, 460, 462, 542, 543, 544, 545, 546, 547, 548, 549, 551, 552, 553, 554, 555, 556, 558, 559, 560, 561], "summary": {"covered_lines": 70, "num_statements": 70, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 6, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [52, 55, 169, 172, 260, 329], "start_line": 20}, "": {"executed_lines": [6, 8, 10, 11, 17, 20, 46, 57, 110, 174, 228, 229, 262, 263, 331, 466], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 12, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [13, 14, 51, 53, 54, 56, 168, 170, 171, 173, 261, 330], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\operations\\tables.py": {"executed_lines": [6, 8, 10, 17, 18, 19, 20, 26, 29, 65, 66, 70, 135, 136, 143, 147, 164, 165, 169, 189, 190, 191, 192, 193, 197, 245, 246, 250, 278, 279, 283, 311, 312, 316, 380, 381, 386, 396, 436, 437, 441, 450, 469, 470, 474, 493, 494, 495, 496, 497, 501, 567, 568, 579, 583, 634, 635, 636, 637, 646, 668, 669, 670, 674, 698, 699, 703, 749, 750, 754, 793, 794, 798, 838, 839], "summary": {"covered_lines": 75, "num_statements": 75, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [22, 23], "functions": {"TableOperations.__init__": {"executed_lines": [66], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 65}, "TableOperations.create": {"executed_lines": [135, 136, 143], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 70}, "TableOperations.delete": {"executed_lines": [164, 165], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 147}, "TableOperations.get": {"executed_lines": [189, 190, 191, 192, 193], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 169}, "TableOperations.list": {"executed_lines": [245, 246], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 197}, "TableOperations.add_columns": {"executed_lines": [278, 279], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 250}, "TableOperations.remove_columns": {"executed_lines": [311, 312], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 283}, "TableOperations.create_one_to_many_relationship": {"executed_lines": [380, 381, 386], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 316}, "TableOperations.create_many_to_many_relationship": {"executed_lines": [436, 437, 441], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 396}, "TableOperations.delete_relationship": {"executed_lines": [469, 470], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 450}, "TableOperations.get_relationship": {"executed_lines": [493, 494, 495, 496, 497], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 474}, "TableOperations.create_lookup_field": {"executed_lines": [567, 568, 579], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 501}, "TableOperations.create_alternate_key": {"executed_lines": [634, 635, 636, 637], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 583}, "TableOperations.get_alternate_keys": {"executed_lines": [668, 669, 670], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 646}, "TableOperations.delete_alternate_key": {"executed_lines": [698, 699], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 674}, "TableOperations.list_columns": {"executed_lines": [749, 750], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 703}, "TableOperations.list_relationships": {"executed_lines": [793, 794], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 754}, "TableOperations.list_table_relationships": {"executed_lines": [838, 839], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 798}, "": {"executed_lines": [6, 8, 10, 17, 18, 19, 20, 26, 29, 65, 70, 147, 169, 197, 250, 283, 316, 396, 450, 474, 501, 583, 646, 674, 703, 754, 798], "summary": {"covered_lines": 27, "num_statements": 27, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [22, 23], "start_line": 1}}, "classes": {"TableOperations": {"executed_lines": [66, 135, 136, 143, 164, 165, 189, 190, 191, 192, 193, 245, 246, 278, 279, 311, 312, 380, 381, 386, 436, 437, 441, 469, 470, 493, 494, 495, 496, 497, 567, 568, 579, 634, 635, 636, 637, 668, 669, 670, 698, 699, 749, 750, 793, 794, 838, 839], "summary": {"covered_lines": 48, "num_statements": 48, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 29}, "": {"executed_lines": [6, 8, 10, 17, 18, 19, 20, 26, 29, 65, 70, 147, 169, 197, 250, 283, 316, 396, 450, 474, 501, 583, 646, 674, 703, 754, 798], "summary": {"covered_lines": 27, "num_statements": 27, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [22, 23], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\utils\\__init__.py": {"executed_lines": [10], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [10], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [10], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\utils\\_pandas.py": {"executed_lines": [6, 8, 9, 11, 12, 15, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 36, 43, 44, 45, 46, 47, 48, 49, 50, 51, 55, 56, 58, 59, 60], "summary": {"covered_lines": 34, "num_statements": 34, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"_normalize_scalar": {"executed_lines": [21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 15}, "dataframe_to_records": {"executed_lines": [43, 44, 45, 46, 47, 48, 49, 50, 51, 55, 56, 58, 59, 60], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 36}, "": {"executed_lines": [6, 8, 9, 11, 12, 15, 36], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [6, 8, 9, 11, 12, 15, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 36, 43, 44, 45, 46, 47, 48, 49, 50, 51, 55, 56, 58, 59, 60], "summary": {"covered_lines": 34, "num_statements": 34, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\__init__.py": {"executed_lines": [4], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [4], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [4], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}}, "totals": {"covered_lines": 3858, "num_statements": 4956, "percent_covered": 77.84503631961259, "percent_covered_display": "78", "missing_lines": 1098, "excluded_lines": 71, "percent_statements_covered": 77.84503631961259, "percent_statements_covered_display": "78"}} \ No newline at end of file diff --git a/docs/async-design-options.md b/docs/async-design-options.md new file mode 100644 index 00000000..cb4a2908 --- /dev/null +++ b/docs/async-design-options.md @@ -0,0 +1,288 @@ +# Async SDK — Implementation Design Options + +**Status:** Decision pending +**Date:** April 2026 + +--- + +## Background + +Adding async support touches every layer of the SDK. The HTTP transport, authentication, data layer, operations, and public client entry point all require async versions. The query builder needs only minimal changes (the fluent chain stays sync; only `execute()` becomes a coroutine). The models layer (`Record`, `TableInfo`, filters, etc.) requires **no changes** — pure dataclasses are shared between sync and async as-is. The existing sync client and all sync behaviour are **untouched**. See Part 3 for a full layer-by-layer breakdown. + +Two decisions are open. Everything else — HTTP transport, auth, operations, query builder — follows directly from these two choices and has no meaningful alternatives. + +1. **How should the async data layer relate to the sync data layer?** The data layer contains a mix of pure logic (payload building, parsing, validation) and I/O calls. The question is whether the async client *inherits* from the sync client and overrides I/O methods, or whether pure logic is *extracted into a shared base* that both sync and async inherit from as siblings. See Part 1. + +2. **Where should async files live in the package tree?** Either all async files go under a dedicated `aio/` sub-package (the Azure SDK convention), or they are placed alongside their sync counterparts in the existing folders. See Part 2. + +--- + +## Part 1 — Implementation Design + +Two options were explored for how the async data layer relates to the sync data layer. + +--- + +### Option A — Async inherits from Sync + +The async client is a subclass of the sync client and overrides every I/O method with an `async def`. + +``` +_ODataClient + └── _AsyncODataClient (overrides all I/O methods) + +_BatchClient + └── _AsyncBatchClient (overrides all I/O methods) +``` + +**Pros** + +- Fewer files — no extra base classes needed. +- DRY without indirection — shared logic lives once in the sync class. +- Sync tests implicitly cover the shared methods. +- Simple inheritance chain, easy to read top-down. + +**Cons** + +- **LSP violation.** The Liskov Substitution Principle (LSP) states that a subclass must be usable wherever its parent class is expected, without breaking the program. Here, `_AsyncODataClient` is a subclass of `_ODataClient`, yet every I/O method changes from a regular function to a coroutine (`async def`). A caller written against `_ODataClient` expects `client.get(...)` to return a result directly; the async subclass returns a coroutine object instead, which is a different type entirely. +- Every overridden method requires `# type: ignore[override]` to silence the type checker (44 suppressions in this codebase). +- Conceptually misleading — "is a" implies substitutability; async-over-sync does not have it. +- Sync and async surfaces must evolve in lockstep or the inheritance chain silently inherits wrong sync behavior. + +--- + +### Option B — Shared Pure Base + Sibling Sync/Async + +Pure (I/O-free) methods are extracted into a base class. The sync and async clients both inherit from that base and are siblings. + +``` +_ODataBase (pure methods: validation, payload building, parsing) + ├── _ODataClient (sync I/O) + └── _AsyncODataClient (async I/O) + +_BatchBase (pure methods: multipart serialisation, response parsing) + ├── _BatchClient (sync I/O) + └── _AsyncBatchClient (async I/O) +``` + +**Pros** + +- Correct OOP — the relationship is "shares pure logic", not "is a". No LSP violation: because async and sync clients are siblings rather than parent/child, neither can be passed where the other is expected, so the type system enforces the boundary automatically. +- Zero `# type: ignore` suppressions — async methods are first-class definitions, not overrides. +- Type-safe out of the box; type checkers validate rather than suppress. +- Matches the Azure SDK pattern (azure-storage-blob, azure-data-tables, etc.). +- Base classes are independently testable. + +**Cons** + +- **Requires refactoring the existing sync layer.** Pure methods must be extracted out of `_ODataClient` and `_BatchClient` and moved into the new base classes. This is a non-trivial change to production code that was not touched by Option A, introducing risk and review surface for the sync path even though its behavior is unchanged. +- Two extra files (`_odata_base.py`, `_batch_base.py`). +- Slightly more indirection — reading the full API surface requires checking both the base and the subclass. +- The pure/IO boundary must be actively maintained as new methods are added. + +--- + +## Part 2 — Folder Structure + +Two options were explored for where async files live in the package tree. + +--- + +### Option A — Dedicated `aio/` folder + +All async code lives under a separate `aio/` sub-package, mirroring the sync layout. + +``` +src/PowerPlatform/Dataverse/ +├── core/ # sync +│ ├── _auth.py +│ └── _http.py +├── data/ # sync data layer +│ ├── _odata.py +│ ├── _odata_base.py # (if using Implementation Option B) +│ ├── _batch.py +│ └── _batch_base.py +├── operations/ # sync public operations (records, tables, …) +└── aio/ # ALL async code + ├── core/ # async counterparts to core/ + │ ├── _async_auth.py + │ └── _async_http.py + ├── data/ # async data layer + │ ├── _async_odata.py + │ └── _async_batch.py + └── operations/ # async public operations +``` + +**Pros** + +- `aio/` is the well-established Python convention (`aiohttp`, `azure-storage-blob`, `motor`, etc.). +- All async code is discoverable from a single entry point. +- Enforces a hard boundary — async and sync cannot accidentally be mixed at import time. +- Users who only want the sync client never need to open the `aio/` tree. + +**Cons** + +- Related sync/async files are in different directories — comparing or maintaining parity requires navigating across the tree. +- Slightly deeper nesting. + +--- + +### Option B — Co-located in existing folders + +Async files live alongside their sync counterparts, distinguished by a naming prefix/suffix. + +``` +src/PowerPlatform/Dataverse/ +├── core/ +│ ├── _auth.py +│ ├── _async_auth.py # next to _auth.py +│ ├── _http.py +│ └── _async_http.py # next to _http.py +├── data/ +│ ├── _odata.py +│ ├── _async_odata.py # next to _odata.py +│ ├── _odata_base.py +│ ├── _batch.py +│ ├── _async_batch.py +│ └── _batch_base.py +└── operations/ + ├── records.py + ├── _async_records.py # next to records.py + └── … +``` + +**Pros** + +- Sync and async counterparts sit side-by-side — parity gaps are immediately visible. +- Flatter structure, fewer directories. +- No sub-package boundary to cross when reading related files. + +**Cons** + +- Does not follow the `aio/` convention expected by most Python developers. +- Each folder mixes sync and async concerns; harder to see the async surface at a glance. +- **Async deps become mandatory for all sync users.** A proper public async API requires exporting async classes from `__init__.py`. Because that file is on the import path for every user, sync-only users who have not installed the async dependency will get a `ModuleNotFoundError` at `from PowerPlatform.Dataverse import DataverseClient` — not at the point of async usage. The only escape is to require users to import from private module paths, which is not a viable public API. Verified empirically: adding one async export to `operations/__init__.py` caused the sync client import to fail when the async dep was absent. +- No package-level boundary to prevent accidental cross-imports. + +--- + +## Appendix — Full SDK Structure Under Each Folder Option + +The trees below show the complete package layout. Async files are marked with `*`. +The sync-only layers (`models/`, `common/`, `core/errors`, `utils/`) are identical in both options and included for completeness. + +### Folder Option A — Dedicated `aio/` sub-package + +``` +src/PowerPlatform/Dataverse/ +├── client.py # DataverseClient (sync entry point) +├── common/ +│ └── constants.py +├── core/ +│ ├── _auth.py +│ ├── _http.py +│ ├── _http_logger.py +│ ├── config.py +│ ├── errors.py +│ └── log_config.py +├── data/ +│ ├── _odata.py +│ ├── _odata_base.py # shared pure base (if Implementation Option B) +│ ├── _batch.py +│ ├── _batch_base.py # shared pure base (if Implementation Option B) +│ ├── _relationships.py +│ ├── _upload.py +│ └── _raw_request.py +├── models/ +│ ├── batch.py +│ ├── filters.py +│ ├── labels.py +│ ├── query_builder.py +│ ├── record.py +│ ├── relationship.py +│ ├── table_info.py +│ └── upsert.py +├── operations/ +│ ├── batch.py +│ ├── dataframe.py +│ ├── files.py +│ ├── query.py +│ ├── records.py +│ └── tables.py +├── utils/ +│ └── _pandas.py +└── aio/ # * all async code lives here + ├── async_client.py # * AsyncDataverseClient (async entry point) + ├── core/ + │ ├── _async_auth.py # * AsyncTokenCredential impl + │ └── _async_http.py # * aiohttp-based HTTP client + ├── data/ + │ ├── _async_odata.py # * async OData client + │ ├── _async_batch.py # * async batch client + │ ├── _async_relationships.py # * async relationships mixin + │ └── _async_upload.py # * async file upload mixin + └── operations/ + ├── async_batch.py # * async batch operations + ├── async_dataframe.py # * async dataframe operations + ├── async_files.py # * async file operations + ├── async_query.py # * async query builder + ├── async_records.py # * async record operations + └── async_tables.py # * async table operations +``` + +--- + +### Folder Option B — Co-located async files + +``` +src/PowerPlatform/Dataverse/ +├── client.py # DataverseClient (sync entry point) +├── async_client.py # * AsyncDataverseClient (async entry point) +├── common/ +│ └── constants.py +├── core/ +│ ├── _auth.py +│ ├── _async_auth.py # * AsyncTokenCredential impl +│ ├── _http.py +│ ├── _async_http.py # * aiohttp-based HTTP client +│ ├── _http_logger.py +│ ├── config.py +│ ├── errors.py +│ └── log_config.py +├── data/ +│ ├── _odata.py +│ ├── _async_odata.py # * async OData client +│ ├── _odata_base.py # shared pure base (if Implementation Option B) +│ ├── _batch.py +│ ├── _async_batch.py # * async batch client +│ ├── _batch_base.py # shared pure base (if Implementation Option B) +│ ├── _relationships.py +│ ├── _async_relationships.py # * async relationships mixin +│ ├── _upload.py +│ ├── _async_upload.py # * async file upload mixin +│ └── _raw_request.py +├── models/ +│ ├── batch.py +│ ├── filters.py +│ ├── labels.py +│ ├── query_builder.py +│ ├── record.py +│ ├── relationship.py +│ ├── table_info.py +│ └── upsert.py +├── operations/ +│ ├── batch.py +│ ├── async_batch.py # * async batch operations +│ ├── dataframe.py +│ ├── async_dataframe.py # * async dataframe operations +│ ├── files.py +│ ├── async_files.py # * async file operations +│ ├── query.py +│ ├── async_query.py # * async query builder +│ ├── records.py +│ ├── async_records.py # * async record operations +│ ├── tables.py +│ └── async_tables.py # * async table operations +└── utils/ + └── _pandas.py +``` diff --git a/docs/async-design.md b/docs/async-design.md new file mode 100644 index 00000000..55a2c3b7 --- /dev/null +++ b/docs/async-design.md @@ -0,0 +1,276 @@ +# Async SDK Design + +| | | +|---|---| +| **Status** | In progress | +| **Date** | April 2026 | +| **Options analysis** | [async-design-options.md](./async-design-options.md) | + +--- + +## Summary + +- `AsyncDataverseClient` will be a new standalone client that mirrors the sync `DataverseClient` API — the sync client and all its behavior are untouched +- Pure logic (payload building, URL construction, parsing) will live in shared base classes (`_ODataBase`, `_BatchBase`) inherited by both sync and async clients independently, not through inheritance of one from the other +- All async code will live under a dedicated **`aio/` sub-package** — async dependencies will be fully isolated and will never affect sync-only users +- `aiohttp` will be an **optional dependency**; sync-only users will install nothing new +- Full design options analysis: [async-design-options.md](./async-design-options.md) + +**What's unchanged:** Models, error classes, configuration, constants, and utilities require no async changes and will be reused as-is by both clients. + +--- + +## Implementation Pattern — Shared Pure Base + Sibling Clients + +Two patterns were considered: + +- **Inheritance pattern** — the async client inherits from the sync client and overrides all I/O methods with async equivalents +- **Sibling pattern** — pure logic is extracted into a shared base; the sync and async clients both inherit from that base independently + +The sibling pattern is proposed. See [async-design-options.md](./async-design-options.md) for the full comparison. + +In the sibling pattern, pure logic will live in `_ODataBase` and `_BatchBase`. The sync and async clients will inherit from the same base and will be siblings. + +``` +_ODataBase (pure: URL building, payload construction, parsing, caches) + ├── _ODataClient (sync I/O) + └── _AsyncODataClient (async I/O) + +_BatchBase (pure: multipart serialisation, response parsing) + ├── _BatchClient (sync I/O) + └── _AsyncBatchClient (async I/O) +``` + +**Why the sibling pattern:** + +- **Correct relationship** — async is not a subtype of sync. The sibling pattern has both clients inherit from a pure base class, a valid is-a relationship that both satisfy. +- **No silent coupling** — in the inheritance pattern, sync changes (a new I/O method, or an existing method gaining I/O) are silently inherited by the async client, potentially blocking the event loop with no error. In the sibling pattern, a missing async method fails immediately. +- **Type safety** — no `# type: ignore[override]` suppressions needed. Async methods are first-class definitions, not overrides with mismatched return types. + +**Tradeoff accepted:** This pattern requires extracting pure logic out of the sync clients. + +--- + +### Folder Structure — Dedicated Async Sub-package + +Two patterns were considered: + +- **Co-location pattern** — async files placed alongside their sync counterparts in the existing folders, distinguished by a naming prefix +- **Dedicated async sub-package pattern** — all async code grouped under a dedicated sub-package, mirroring the sync layout + +The dedicated async sub-package pattern is proposed. See [async-design-options.md](./async-design-options.md) for the full comparison. + +All async code will live under `aio/`, mirroring the sync layout. + +``` +src/PowerPlatform/Dataverse/ +├── client.py # DataverseClient (sync entry point) +├── data/ +│ ├── _odata_base.py # shared pure base +│ ├── _odata.py # sync OData client +│ ├── _batch_base.py # shared pure base +│ └── _batch.py # sync batch client +├── operations/ # sync operations +│ ├── records.py +│ ├── tables.py +│ ├── query.py +│ ├── batch.py +│ ├── dataframe.py +│ └── files.py +└── aio/ # ALL async code + ├── async_client.py # AsyncDataverseClient (async entry point) + ├── core/ + │ ├── _async_auth.py # AsyncTokenCredential implementation + │ └── _async_http.py # aiohttp-based HTTP client + ├── data/ + │ ├── _async_odata.py # async OData client + │ └── _async_batch.py # async batch client + └── operations/ + ├── async_records.py + ├── async_tables.py + ├── async_query.py + ├── async_batch.py + ├── async_dataframe.py + └── async_files.py +``` + +**Why the dedicated async sub-package:** + +- **Dependency isolation** — the `aio/` boundary ensures `aiohttp` is never imported by the sync path. With co-location, `__init__.py` eager imports or accidental cross-imports can pull async deps into the sync path, causing `ImportError` for users who never installed `aiohttp`. +- **Azure SDK convention** — `azure-storage-blob`, `azure-data-tables`, and other Azure SDKs expose async clients under `aio/` sub-package ([Python Guidelines](https://azure.github.io/azure-sdk/python_design.html)), lowering the learning curve for developers who use other Azure SDKs. +- **Discoverability** — the full async surface is visible in one directory tree, not scattered across every folder in the project. + +**Tradeoff accepted:** Sync and async counterparts live in different directories. + +--- + +## SDK Components + +| Component | Existing files | Async change | +|---|---|---| +| Entry point | `client.py` | New `AsyncDataverseClient` entry point | +| Core | `_auth.py`, `_http.py`, `_http_logger.py`, `config.py`, `errors.py`, `log_config.py` | New async auth (`AsyncTokenCredential`) and async HTTP client (`aiohttp`); rest reused as-is | +| Data layer | `_odata.py`, `_batch.py`, `_relationships.py`, `_upload.py`, `_raw_request.py` | New async OData, batch, relationships, and upload inheriting shared pure bases; `_raw_request.py` reused as-is | +| Operations | `records.py`, `tables.py`, `query.py`, `batch.py`, `dataframe.py`, `files.py` | New async counterpart for each — thin `async def` + `await` delegation wrappers | +| Models | `query_builder.py`, `record.py`, `filters.py`, `batch.py`, `relationship.py`, `table_info.py`, `upsert.py` | All reused as-is; `execute()` and `to_dataframe()` in query builder become coroutines in the async path | +| Common | `constants.py` | Reused as-is | +| Utils | `_pandas.py` | Reused as-is | + +--- + +## Dependencies + +| Dependency | Type | Required by | +|---|---|---| +| `aiohttp>=3.9` | Optional runtime | `aio/` only — never imported by the sync path | +| `pytest-asyncio` | test | Async test suite | + +`aiohttp` will be listed as an optional extra in `pyproject.toml`. Sync-only users who do not install the extra will never encounter an import error originating from the async path. + +--- + +## Implementation Notes + +### `ClientSession` Lifecycle + +`aiohttp.ClientSession` requires explicit closure to drain in-flight requests and release connections. Both usage patterns are supported: + +```python +# Context manager (preferred — session lifecycle is explicit) +async with AsyncDataverseClient(url, credential) as client: + await client.records.get(...) + +# Standalone (supported — caller is responsible for closing) +client = AsyncDataverseClient(url, credential) +try: + await client.records.get(...) +finally: + await client.close() +``` + +One `ClientSession` will be shared across all requests for the client's lifetime. Creating a new session per request defeats connection pooling and is an antipattern in `aiohttp`. The session is created lazily on the first request for standalone usage, or in `__aenter__` for context manager usage, and passed down to `_AsyncODataClient` → `_AsyncHttpClient`, which uses it but does not own it. + +**Timeouts:** Timeouts will be configured per-request via `aiohttp.ClientTimeout`, matching the sync client's per-method defaults (120s for writes, 10s for reads). `aiohttp` automatically discards failed or timed-out connections from the pool — no manual pool recovery is needed. + +--- + +### Concurrency — `asyncio.gather` + `Semaphore` + +For bulk record operations (`records.create()`, `records.update()`, `records.upsert()`), the sync SDK uses `ThreadPoolExecutor(max_workers=N)` to dispatch record chunks concurrently. The async equivalent will replace the thread pool with `asyncio.gather()` and an `asyncio.Semaphore` to enforce a concurrency cap (`max_workers` defaults to `10`; async coroutines are cheap so the cap is driven by Dataverse server-side throttling limits, not client resource constraints). + +```python +semaphore = asyncio.Semaphore(max_workers) + +async def _bounded(chunk): + async with semaphore: + return await _execute_with_retry_async(chunk) + +await asyncio.gather(*[_bounded(chunk) for chunk in chunks]) +``` + +**429 throttling:** Transient errors (429, 503, 504) will trigger a retry with backoff. No new chunk will be dispatched while one is waiting out its backoff — the concurrency cap is maintained during retries, matching the sync throttling behavior. + +--- + +### QueryBuilder + +`QueryBuilder.execute()` and `QueryBuilder.to_dataframe()` are sync methods that call into the sync client. In the async path, `AsyncQueryOperations.builder()` returns an `AsyncQueryBuilder` subclass that overrides both as `async def`, delegating to the async client. + +**At GA**, `QueryBuilder` will be replaced by an inert `SelectQuery` builder, with `build()` remaining on `SelectQuery` and execution moving to `QueryOperations.execute()` / `AsyncQueryOperations.execute()`. `SelectQuery` is shared between sync and async — no async variant is needed. The async path adds only `await`: + +```python +result = await client.query.execute( + select("name", "revenue") + .from_("account") + .where(eq("statecode", 0)) +) +for record in result: + print(record["name"]) +``` + +--- + +### Error Handling and Cancellation + +**`asyncio.CancelledError`** propagates — the SDK never suppresses it. Since Python 3.8 it is a `BaseException`, so the retry loop's `except aiohttp.ClientError` clause will not catch it. A cancelled request's connection is discarded from the pool; the `ClientSession` remains valid for future requests. Final cleanup is handled by `async with` or `await client.close()`. + +**Error types:** No new async-specific error types are introduced. + +| Failure type | Sync | Async | +|---|---|---| +| HTTP error (4xx, 5xx) | `HttpError` | `HttpError` — reused as-is | +| Network error (connection, timeout) | `requests.exceptions.RequestException` | `aiohttp.ClientError` — same pattern, different library | +| Cancellation | N/A | `asyncio.CancelledError` — propagates as `BaseException` | + +`HttpError`, `ValidationError`, `MetadataError`, and `SQLParseError` are all reused unchanged. + +--- + +### Pagination + +OData pagination is sequential — each `@odata.nextLink` URL is only known after receiving the previous page's response. The async implementation will use an async generator, structurally identical to the sync version with `await` on each page fetch. + +The SDK has two pagination patterns, each consumed differently: + +- **OData (`records.get`)** — async generator; iterated with `async for`, yields one page at a time. Each `@odata.nextLink` is fetched sequentially. + + ```python + async with AsyncDataverseClient(url, credential) as client: + async for page in client.records.get( + "account", + filter="statecode eq 0", + select=["name", "telephone1"], + page_size=50, + ): + for record in page: + print(record["name"]) + ``` + +- **SQL (`query.sql`)** — coroutine; called with `await`, returns a flat list after all pages are collected internally. + + ```python + rows = await client.query.sql("SELECT TOP 100 name FROM account") + for row in rows: + print(row["name"]) + ``` + +--- + +## Testing Strategy + +- Unit tests for all async implementations with >= 90% coverage +- Integration tests similar to the existing sync integration tests — same scenarios and coverage, adapted to use `AsyncDataverseClient` with `async`/`await` + +--- + +## Implementation Phases + +| Phase | Deliverable | +|---|---| +| 1 — Refactoring | Extract pure logic into `_ODataBase` and `_BatchBase`; sync client inherits from base; all existing sync tests should pass | +| 2 — Async implementation | `aio/` sub-package with async HTTP, auth, data layer, operations, and `AsyncDataverseClient` entry point | + +--- + +## Examples + +### Basic usage + +```python +from PowerPlatform.Dataverse.aio import AsyncDataverseClient + +async with AsyncDataverseClient(url, credential) as client: + record_id = await client.records.create("account", {"name": "Contoso"}) + record = await client.records.get("account", record_id) + await client.records.delete("account", record_id) +``` + +### Batch + +```python +async with AsyncDataverseClient(url, credential) as client: + batch = client.batch.new() + batch.records.create("account", {"name": "A"}) + batch.records.create("account", {"name": "B"}) + result = await batch.execute() +``` diff --git a/docs/design-evaluation.md b/docs/design-evaluation.md new file mode 100644 index 00000000..dbdca80d --- /dev/null +++ b/docs/design-evaluation.md @@ -0,0 +1,344 @@ +# Dataverse Python SDKs — Design Evaluation + +**Status**: Draft for review + +--- + +## 1. Summary + +Two Python SDKs for Dataverse are in flight: a **client SDK** for scripts, notebooks, and integrations (v0.1.0b8, approaching GA) and a **server-side runtime** for Python plugins running in the Dataverse sandbox (greenfield, in design). Both face the same typing question — stay with the current dict-based API (`client.records.create("account", {"name": "X"})`) or adopt strongly-typed entity classes (`client.records.create(Account(name="X"))`) — and either SDK could plausibly answer differently from the other. + +This document recommends an **asymmetric** answer: + +- **Client SDK at GA — dict API + SQL-style query surface.** The dict write API (`client.records.create("account", {...})`) ships as the GA contract. The beta's fluent `QueryBuilder` is **replaced pre-GA** by a SQL-style query surface (`select(...).where(...)`) matching the server SDK — one query idiom across script and plugin code. GA timeline adjusts to accommodate; the permanent cost of shipping two query surfaces outweighs the schedule delta. +- **Client SDK typing — evidence-gated.** Whether to *add* a strongly-typed entity layer on top of the dict API is deferred to a head-to-head evaluation (§3.1), not shipped on theoretical benefit. A `DataverseModel` Protocol ships pre-GA as a ~10-line seam so the option stays open without a future API break. +- **Server SDK — ship typed.** The production-reliability case (§3.2) justifies typing independent of agent-authoring behavior. +- **Both SDKs share the same query idiom and shared model package.** SQL-style (`select` / `insert_into` / `update` / `delete`) is the single query surface. `Entity`, `FilterExpression`, `Record`, and the SQL-style free functions live in one shared package consumed by both SDKs. + +### 1.1 Decisions at a glance + +| Question | Decision | +|---|---| +| Delay GA for **typing** work? | No — typing decision is evidence-gated (§3.1) | +| Delay GA for the **SQL-style query-surface** replacement? | Yes — accept a schedule shift; the permanent two-surface tax outweighs the delta (§3.3) | +| One pre-GA change worth making (beyond the query surface)? | Yes — define `DataverseModel` Protocol and widen write-signatures to accept it (~10 lines, zero behavioral change) | +| Ship a typed entity layer in the client SDK? | Evaluation-gated — see §3.1 | +| Ship a typed entity layer in the server SDK? | Yes — see §3.2 | +| Require code generation to use either SDK? | No. Codegen is always opt-in, build-time, scoped via `--tables` | +| Shared `Entity` / query IR across client and server? | Yes — one package, one generator, one output format | +| Primary query idiom in both SDKs? | SQL-style (`select` / `insert_into` / `update` / `delete`) — **sole** programmatic query builder at GA | +| Keep fluent `QueryBuilder` from beta at GA? | No. One query idiom, not two. Beta callers migrate via codemod | +| Runtime metadata fetching at client startup? | No — defeats static analysis and introduces cold-start tax | +| Deprecate the dict API ever? | No | + +### 1.2 Phased plan + +1. **Client SDK GA** — ship the dict API. Replace the beta's fluent `QueryBuilder` with SQL-style (`select` / `insert_into` / `update` / `delete`) as the sole programmatic query builder. Add the `DataverseModel` Protocol seam (§4). Publish a codemod to migrate beta users. +2. **Evaluation (1–2 weeks)** — run 20 representative tasks across three API arms; decide whether typed client-side adds measurable value (§3.1). +3. **Client SDK post-GA minor** — path branches on the evaluation. Either stop at GA's dict + SQL-style surface, or additionally ship the typed entity layer. +4. **Server SDK alpha** — typed-first, SQL-style, shared query IR with client. Independent of client evaluation result. + +--- + +## 2. The three approaches + +### 2.1 Dict-based (current shipping client SDK) + +```python +client.records.create("account", {"name": "Contoso", "revenue": 1_000_000}) +row = client.records.get("account", guid) +row["name"] # string keys; no IDE autocomplete; typos surface at runtime +``` + +Pros: zero ceremony, excellent for scripts and notebooks, trivially Pythonic. +Cons: no compile-time validation, no autocomplete. + +### 2.2 Strongly-typed with mandatory codegen + +Two sub-variants; both are rejected. + +**Runtime generation** (types built from a live metadata fetch at startup): + +```python +Account = await client.get_type("account") # network round-trip required +client.records.create(Account(name="Contoso")) +``` + +Failure modes: +- Static analyzers (mypy / pyright / Pylance) cannot see types built by `type(...)` at runtime — they resolve to `Any`. The headline benefit of typing does not arrive. +- Per-cold-invocation metadata fetch in serverless environments. +- Schema drift between runs is silent and non-reviewable. +- Offline testing requires a metadata mock or a live org. + +**Build-time with hard cutover** (generator emits `.py` files; dict API removed): +- A breaking change for existing users with no benefit over the additive approach below. + +### 2.3 Additive typed layer (recommended shape if typing ships) + +```python +# Dict form continues to work unchanged +client.records.create("account", {"name": "Contoso"}) + +# Typed form is opt-in +class Account(Entity, table="account", primary_key="accountid"): + name = Text(max_length=160) + revenue = Money() + +client.records.create(Account(name="Contoso", revenue=1_000_000)) +``` + +- All operations accept `Union[str, type[Entity]]` and `Union[dict, Entity]`. +- Code generation is build-time, produces editable Python files checked into source control, and is scoped with `--tables` (no recursive relationship descent). +- Types are versioned snapshots of server schema; regenerating is the sync command. + +This matches the pattern used by Prisma, gRPC/protobuf, boto3-stubs, and the .NET Dataverse SDK itself (see Appendix A). + +--- + +## 3. Why asymmetric + +### 3.1 Client-side: the agent-grounding argument has weakened + +The strongest original argument for client-side typing was that agents would make fewer schema mistakes. Observed behavior of the shipping Dataverse Skills plugin contradicts this — agents write correct Python against the dict API on common scenarios. + +Grading what remains: + +| Claim | Holds once agents are already competent with dicts? | +|---|---| +| IDE autocomplete on 500-field entities | No — agents don't use IDEs | +| mypy / pyright catches typos pre-deploy | Only if CI runs typechecking on the agent-authored call sites, which most teams don't | +| Refactoring support on column renames | No — agents regenerate code rather than refactoring it | +| Self-documenting schema in source | Weak — duplicates metadata available via MCP and the portal | +| .NET SDK parity | Aesthetic; the .NET SDK's early-bound value was human-IDE autocomplete, an agent-era artifact | +| Less agent hallucination | Contradicted by observed behavior | +| Token efficiency on complex multi-entity workflows | Holds — types in workspace eliminate exploratory metadata round-trips | +| Enterprise / ISV long-term code maintenance | Holds — stable contracts and static verifiability matter at scale | + +The remaining value is real but narrower than the initial pitch. Whether it justifies the investment is an empirical question. + +**Proposed evaluation** (1–2 weeks, cheap relative to the 6–8 weeks of post-GA work a typed-client layer would require): + +- **20 representative tasks**: simple CRUD, multi-entity workflows, polymorphic lookups, option-set enums, bulk operations, tenant-custom schemas. +- **Three API arms, answering two distinct questions**: + - **(A) dict + current fluent `QueryBuilder`** — *reversibility check* on the SQL-style commitment in §3.3. If A materially beats B, the plan to remove fluent at GA should be reopened. + - **(B) dict + SQL-style** — the GA baseline. + - **(C) typed + SQL-style** — the additive typed layer under evaluation. +- **Measure**: task success rate, tokens consumed, correction cycles, wall-clock. +- **Decision thresholds** (concrete, not qualitative): + - **Ship typed client-side only if (C) vs (B)**: task-success parity (within ±3 pp) AND tokens ≥15% lower **OR** correction cycles ≥20% lower. + - **Reconsider the SQL-style commitment only if (A) vs (B)** clears the same bar in favor of A. Expected outcome: parity or minor B advantage; the industry precedent in §3.3 would need direct contradiction to move. + - **If results are mixed** (e.g., C wins on tokens but loses on success): hold the typed client. The Protocol seam (§4) keeps the option open for re-evaluation with better tooling later. + +### 3.2 Server-side: production reliability stands independently + +Plugin code runs on live business events. Five structural asymmetries make left-shifted error detection more valuable server-side than client-side: + +1. **Test coverage is structurally harder.** Plugin context, pre/post-images, sync-vs-async triggers, and organization-service state are complex to mock. Agent-generated plugins rarely ship with comprehensive tests; script tests are easier and typically have better coverage. +2. **Silent-failure modes are common.** Plugin code can catch-and-log exceptions and continue in corrupted state. Scripts crash loudly and immediately. +3. **Blast radius differs by an order of magnitude.** Script failure → developer fixes script. Plugin failure → failed business transaction, possibly across many records, possibly customer-visible. +4. **Feedback-loop latency.** Script error: seconds (someone is watching the terminal). Plugin error: minutes to days (trace log, incident triage). Types collapse detection to edit time regardless. +5. **Deploy / rollback cycle.** Plugins take minutes to re-register and re-deploy. Every typo caught at typecheck-time saves real wall-clock. + +None of these are absolute — good tests and CI can catch typos without types. But the expected value of left-shifted detection is meaningfully higher server-side. This is the same argument that has kept early-bound entities the dominant plugin-authoring style on the Dataverse .NET SDK for 15 years, and it is independent of how well or poorly agents write plugin code. + +**Costs we accept on the server side.** Typing is not free. Shipping server-typed adds (a) a generator we must keep aligned with client-side codegen, (b) shared-package release coordination so client and server stay on compatible Entity contracts, (c) generated files in plugin authors' repos that require re-running the generator on schema changes, and (d) documentation surface covering both typed-only and dict-interop patterns. These are real, but each is bounded: the generator is shared with the client (§3.5), and schema-drift handling is already required for any plugin that reads metadata. The production-reliability wins outweigh these specific costs; the calculus would be different client-side, which is why §3.1 exists. + +**What "ship typed server SDK" means concretely** — same `Entity` base class and field descriptors as the client-side typed layer (if one ships) or as the shared model package regardless; SQL-style query surface (§3.3); generator CLI; a starter set of pre-generated platform-entity types (§5.5 applies); plugin-context execution model that accepts both Entity instances and dicts for the first release. Appendix B points at a prototype demonstrating this shape. + +### 3.3 Query syntax: one idiom, both SDKs + +Current divergence: + +| | Client SDK shape | Server SDK shape | +|---|---|---| +| Read | `client.query.builder(Account).where(Account.name == "X").execute()` | `ctx.dataverse.sql(select(Account).where(Account.name == "X")).execute()` | +| Create | `client.records.create(Account(name="X"))` | `ctx.dataverse.sql(insert_into(Account).value(Account.name, "X")).execute()` | +| Update from fetched | `client.records.update(obj)` | `ctx.dataverse.sql(update(Lead).from_entity(payload).where(Lead.leadid == id)).execute()` | + +Both compile to the same conceptual query; only the surface differs. Users and agents copying code across the boundary feel the seam. + +**Recommendation: adopt SQL-style (`select` / `insert_into` / `update` / `delete`) as the one primary query idiom in both SDKs.** + +Reasons: + +1. **Re-learnability is zero** — plugin code and script code look identical. +2. **SQLAlchemy 2.0 deliberately moved in this direction**, from fluent `session.query(User).filter(...)` to `select(User).where(...)`. Stated rationale: maps more directly to SQL, composes better, more explicit about execution. +3. **SQL is universally known** by developers and by LLMs — no new vocabulary. +4. **Client SDK is pre-GA.** Beta callers are signing up for churn; the cost of changing the recommended surface now is bounded. + +The existing fluent `QueryBuilder` in the beta is **removed before GA**. One programmatic query builder, not two. Beta users migrate via a mechanical rewrite, publishable as a codemod (an AST-based code-transformation tool — LibCST / Bowler in Python); the transformation is a 1:1 method-to-function mapping, e.g. `builder("account").filter_eq("statecode", 0).execute()` → `select("account").where(eq("statecode", 0)).execute()`. The beta population is small (~500 users) and signed up for churn; the permanent two-surface documentation/test/agent-training cost of keeping both outweighs the one-time migration burden. + +### 3.4 The consolidated GA surface + +Everything the SDK ships at GA, and why each surface is defensible once the fluent `QueryBuilder` is cut: + +| Surface | Why it stays | +|---|---| +| **SQL-style query** (`select(...).where(...).top(...)` etc.) | The one programmatic query builder | +| **FilterExpression primitive** (`eq`, `gt`, `contains` + `& \| ~`) | Shared building block consumed by `where(...)`; already shipping, reused unchanged | +| **Imperative CRUD** (`client.records.{create, update, get, delete, upsert}`) | Single-record ergonomics; the `session.add(obj)` vs `insert(...)` split in SQLAlchemy | +| **Raw SQL string** (`client.query.sql("SELECT …")`) | Power-user escape hatch at a different abstraction level | +| **DataFrame adapter** (`client.dataframe.*`) | Output-shape concern, not a competing query syntax; accepts SQL-style queries | +| **Metadata operations** (`client.tables.*`) | Unrelated to data query/write | +| **File upload** (`client.files.*`) | Unrelated to data query/write | + +Two adjacent cleanups to do while the query surface is in motion: + +1. The existing `QueryBuilder` already has two internal ways to filter — 15 `filter_*(column, value)` methods and `where(FilterExpression)`. Only the `where(...)` form carries into SQL-style's surface. The `filter_*` method variants do not need to be reproduced. +2. `client.dataframe.*` should accept SQL-style queries (`client.dataframe.query(select(...).where(...))`) rather than carry its own query DSL. + +### 3.5 What is shared between client and server SDK regardless + +These pieces belong in a single package consumed by both SDKs: + +- `Entity` base class and field descriptors (`Text`, `Integer`, `Money`, `Lookup`, `Picklist`, `Boolean`, `DateTime`, `Guid`, …) +- `Record` read-wrapper with `.id`, `.table`, `.etag`, dict-like access +- `FilterExpression` tree and class-level operator overloads (`Account.name == "X"` → expression object) +- `to_create_payload()` / `to_update_payload()` helpers that strip non-writable fields +- The SQL-style free functions (`select`, `insert_into`, `update`, `delete`) +- **One** metadata-driven code generator, **one** output file format + +Per-transport compilation (OData `?sql=` on client; query expression / fetch XML in-process on server) stays in each SDK's `operations/` layer. + +Two generators emitting subtly different Python is the concrete failure mode to avoid. + +### 3.6 Metadata-management boundary (server SDK) + +Server SDK v1 is read/write-data-only. Plugins do not mutate schema. This matches the .NET plugin model. Revisit in a later release if a schema-as-code story emerges. + +--- + +## 4. The `DataverseModel` Protocol — the one pre-GA change + +The single typing-related change worth making before client SDK GA: + +```python +from typing import Any, ClassVar, Protocol, Self, Union + +class DataverseModel(Protocol): + """Any class that can be passed to a Dataverse write operation. + + Generated entity classes implement this protocol. Users may also hand-roll + their own models (dataclass, Pydantic BaseModel, etc.) and pass them + interchangeably with raw dicts. + """ + __entity_logical_name__: ClassVar[str] + __entity_set_name__: ClassVar[str] + + def to_dict(self) -> dict[str, Any]: ... + @classmethod + def from_dict(cls, data: dict[str, Any]) -> Self: ... + +# Widened signatures — dict callers unchanged; typed callers supported +Payload = Union[dict[str, Any], DataverseModel] + +def create( + table_or_model: Union[str, type[DataverseModel]], + data: Union[Payload, list[Payload]], +) -> Union[str, list[str]]: ... +``` + +**No code generator ships. No generated classes ship. No user-visible behavior changes.** Existing dict callers continue to work unchanged. + +Why it is worth the pre-GA effort: the Protocol is the permanent contract between the SDK and any future typed-codegen approach. If it ships in GA, codegen can land in a minor release without a public-API change. If it does not, every future typed-API approach requires a signature-widening and a deprecation cycle. Cheap now, expensive to retrofit. + +--- + +## 5. Implementation considerations + +### 5.1 Codegen will not cascade into the whole org + +Navigation-property fields reference target tables by **logical-name string**, not by imported class. Users list entities explicitly (`--tables a b c`). No recursive descent into the relationship closure. Same mechanism as protobuf's explicit imports. + +### 5.2 Generated file trees are not large + +A generated entity file is roughly 20 KB. 500 entities ≈ 10 MB. Non-issue for disk, git, or IDE indexing. Users generate only the tables they use. + +### 5.3 Schema drift is detectable and reviewable + +Dataverse's `RetrieveMetadataChanges` API supports e-tag-based incremental metadata sync. Generator re-runs are fast. Three sub-cases: + +| Change | Dict API | Typed API | +|---|---|---| +| Server adds column, caller doesn't reference it | Harmless | Harmless (typed access unknown, dict access via `record.data[...]` still works) | +| Server adds column, caller wants to use it | Works immediately | Re-run generator; commit diff | +| Server removes column that caller references | 400 at runtime | Re-run generator, code fails at typecheck OR runtime — strictly more information than the dict case | + +### 5.4 "What is the source of truth?" + +The server is the source of truth for platform-managed metadata. The generated Python file is a versioned snapshot — same pattern as `git submodule`, `go.sum`, or a generated gRPC stub. The local file is authoritative for compilation; the server is authoritative for the live schema. They can drift, and the generator is the sync command. + +For user-defined custom entities (a future schema-as-code direction), local files could be push-authoritative. Out of scope for this decision. + +### 5.5 Pre-shipped out-of-box entity types (conditional) + +Applies only if the client-side evaluation justifies a typed client SDK. + +Concern: Dataverse schemas are tenant-specific. Every org has different custom columns on the same "platform" entity. A pre-shipped `Account` class might mislead users about what their schema actually contains. + +Resolution: + +- Ship types covering **platform-guaranteed columns only** (`accountid`, `name`, `createdon`, `modifiedon`, `statecode`, `statuscode`, `ownerid`, …). +- Each shipped file carries a docstring: *"This class contains platform-guaranteed columns only. Run the generator to extend with tenant-specific custom columns."* +- Typed access for unknown fields falls through to `record.data[...]` — no correctness cliff, only missing autocomplete for tenant-custom columns. +- The model is boto3-stubs: cover the stable core; users accept that custom/evolving surface requires regeneration. + +If false-completeness concerns show up in practice, deprecate the pre-shipped set and require user-side regeneration. Cheap to reverse. + +### 5.6 Security + +The server SDK's SQL-style surface routes through the same governed query path as the client. Parameterization is mandatory; there is no raw-SQL executor. The typing decision is orthogonal to injection defense. + +--- + +## 6. Non-goals + +- Shipping a runtime metadata cache +- Auto-generating the full relationship closure +- Converging the C# and Python SDK surfaces +- Requiring users to run a code generator before their first script + +--- + +## 7. Follow-ups + +1. **Evaluation design and execution.** 20 representative tasks × 3 API arms. Single highest-leverage action; determines whether client-side typing ships at all. 1–2 week window. Needs an owner. +2. **Pre-GA Protocol seam implementation.** ~1 week. Blocker only for permanent typed-codegen optionality at GA time. +3. **SQL-style query surface on the client SDK.** Built on the existing `FilterExpression` primitives; replaces the beta's fluent `QueryBuilder`. Ships at GA. GA timeline may need adjustment to accommodate; accepted in exchange for a clean single-query-idiom contract. Needs a codemod for beta users. +4. **Shared model package** (Entity, field descriptors, filter expressions, SQL-style functions). Owner, repo, release cadence — blocker for client/server symmetry under every path. +5. **Generator consolidation.** Two exploratory generators exist; reconcile to one shared tool with the best of each. +6. **Generator distribution.** PyPI extra (`pip install PowerPlatform-Dataverse-Client[gen]`) vs. a separate `dataverse-gen` package. Lean toward extra. +7. **Plugin metadata boundary.** Confirm server SDK is read/write-data-only for v1. +8. **GA version number and release cadence.** Does GA ship as `1.0.0` (stable-API signal) or continue the current `0.1.x` line? Post-GA minor cadence? This doc defers to the SDK team's PyPI/release policy. +9. **Codemod tooling and publication.** Which codemod framework (LibCST, Bowler, other) and where is it distributed (PyPI package? `pip install PowerPlatform-Dataverse-Client[migrate]`? a one-shot script in the repo)? + +--- + +## Appendix A — Prior art + +| Ecosystem | Typing approach | Lesson | +|---|---|---| +| .NET Dataverse SDK (15+ years) | Build-time: `CrmSvcUtil` / `pac modelbuilder build` emits early-bound classes. Late-bound `entity["name"]` remains alongside forever. | The closest structural precedent. Both idioms coexist; neither was ever deprecated. | +| Prisma (TypeScript) | Build-time: `prisma generate` emits a typed client from `schema.prisma`. Zero runtime generation. | Widely cited as best-in-class backend DX. | +| gRPC / protobuf | Build-time: `.proto` → generated Python classes. Runtime generation is explicitly unsupported. | Industry default for cross-language contracts. | +| boto3-stubs / mypy-boto3-builder | Stringly-typed at runtime; separate PyPI package ships pre-generated `.pyi` stubs. | Demonstrates additive typed layer on a scripting-first SDK. | +| SQLAlchemy `automap_base()` | Runtime reflection from a live DB schema. | Cautionary anti-pattern; consistently the least-used, least-trusted part of SQLAlchemy because static analyzers cannot see the result. Same failure mode as Option B-runtime in §2.2. | + +Two observations from the list: + +1. Every well-regarded typed SDK generates at **build time**. We could find no successful counter-example. +2. The closest structural analogue to Dataverse — its own .NET SDK — ships both early-bound and late-bound and has kept both. That is the right model for Python too. + +## Appendix B — Existing prototype references + +These branches and packages are exploratory prior work, not plans of record. Each has design ideas worth borrowing; none is the committed direction for either SDK. + +- **Client SDK main** (`microsoft/PowerPlatform-DataverseClient-Python` at v0.1.0b8): dict API, fluent `QueryBuilder` with composable filter expressions, SQL endpoint via OData `?sql=`, Pandas bridge, batch operations, upsert with alternate keys, file upload, context-manager connection pooling. +- **Client SDK typed-layer prototype branch** (`users/*/typed_entity_model`): working additive typed layer — `Entity` base class, field descriptors (`Text`, `Integer`, `Money`, `Lookup`, `Picklist`, `Boolean`, `DateTime`, …), class-level operator overloads, generator CLI, ~1,300 unit tests passing. All operations accept `Union[str, type[Entity]]`. Demonstrates the additive shape from §2.3. +- **Server-side plugin runtime prototype** (`Dataverse.Sandbox.Runtime`): typed-first, SQL-style query surface, pre-generated types for a starter set of ~16 platform entities, metadata-driven generator tool, plugin-context execution model. + +## Appendix C — Rejected options + +- **Runtime type generation.** Defeats static analysis; introduces cold-start tax; silent schema drift; breaks offline testing. Types built via `type(...)` from a live metadata fetch are opaque `Any` to mypy/pyright/Pylance. +- **Mandatory typed-only client SDK (hard cutover).** Breaking change for existing dict callers with no benefit over the additive shape. +- **Two generators, two Entity models, two output formats.** Subtly different emitted Python across client and server is the specific failure mode that reintroduces the cross-context re-learning cost we are trying to remove. \ No newline at end of file diff --git a/docs/proposal-typed-model-alignment.md b/docs/proposal-typed-model-alignment.md new file mode 100644 index 00000000..fdb53031 --- /dev/null +++ b/docs/proposal-typed-model-alignment.md @@ -0,0 +1,277 @@ +# Proposal: Typed Entity Model — Alignment with Server SDK and GA Scope + +**Status:** Draft for discussion +**Date:** 2026-04-20 + +--- + +## Background + +The Python **client SDK** (`PowerPlatform.Dataverse`, `main` branch) uses a string-based programming model: table names, column names, and filter expressions are all plain strings at runtime. The **server SDK** (`Dataverse.Server.Runtime.Extension`) uses a strongly typed entity model where each table is a generated Python class, fields are typed descriptors, and queries are expressed as composable condition objects. + +A prototype branch explored what it would take to add typed entity support to the client SDK. It produced a working implementation — but the baseline for this proposal is the **`main` branch**, not the prototype. The prototype is evidence of feasibility and reveals the specific gaps. + +--- + +## API Comparison: Client SDK vs. Server SDK + +The table below covers both SDKs as they exist today (`main` branch for the client, current state for the server). + +| Category | Feature | Client SDK | Server SDK | +|---|---|---|---| +| **Entity / Type Model** | Entity base class | No (main); prototype branch adds it | Yes — `Entity` with `_logical_name`, `_entity_set`, `_primary_id`, `_primary_name` | +| | Field descriptors | No (main); prototype adds `Text`, `Memo`, `Integer`, `BigInt`, `DecimalNumber`, `Double`, `Money`, `DateTime`, `Guid` | Yes — same set | +| | `Lookup` / `CustomerLookup` | No (main); prototype adds both | Yes | +| | `PicklistBase` / `PicklistOption` / `MultiPicklist` | No (main); prototype adds all | Yes | +| | `BooleanBase` / `BooleanOption` | No (main); prototype adds both | Yes | +| | `Entity.as_dict()` | No (main); prototype adds | Yes | +| | `Entity.to_create_payload()` / `to_update_payload()` | No (main); prototype adds | Yes | +| | `Entity.from_dict()` | No (main); prototype adds | Yes | +| | `Entity.fields()` | No (main); prototype adds | Yes | +| | Entity class declaration syntax | N/A (main); prototype adds keyword-arg style: `class Foo(Entity, table="…")` | Plain class attributes only: `_logical_name = "…"` | +| **Query / Filter DSL** | Comparison operators on field descriptors (`==`, `!=`, `>`, etc.) | No (main); prototype adds via `_ComparisonFilter` | Yes — via `Condition` | +| | Logical composition (`&`, `\|`, `~`) | Yes (on `FilterExpression` objects, string-based) | Yes (on `Condition` / `CompositeCondition`) | +| | Filter rendering target | OData `$filter` strings | Parameterized SQL fragments (`.to_sql()`) | +| | `filter_in` / `filter_not_in` | Yes | No | +| | `filter_between` | Yes | No | +| | String function filters (`contains`, `startswith`, `endswith`) | Yes | No | +| | Null checks | Yes | No | +| | Raw filter passthrough | Yes | No | +| **CRUD Operations** | Single record create | Yes — `client.records.create(table, dict)` | No | +| | Bulk create (`CreateMultiple`) | Yes | No | +| | Single record read | Yes — `client.records.get(table, id)` | No | +| | Multi-record read (paginated) | Yes | No | +| | Single record update (PATCH) | Yes | No | +| | Bulk update (`UpdateMultiple`) | Yes | No | +| | Single record delete | Yes | No | +| | Bulk delete (`BulkDelete` async job) | Yes | No | +| | Upsert (alternate key) | Yes — `client.records.upsert()` with `UpsertItem` | No | +| | Typed entity class accepted in place of table string | Prototype only | N/A — no execution layer | +| **Query Execution** | OData `$filter` queries | Yes | No | +| | SQL queries (read-only, constrained subset) | Yes — `client.query.sql()` | No | +| | `$expand` (related record navigation) | Yes | No | +| | `$orderby` / `$top` / `$count` | Yes | No | +| | Pagination (`$skiptoken`) | Yes | No | +| | Fluent QueryBuilder | Yes | No | +| | `QueryBuilder.to_dataframe()` | Yes | No | +| | Aggregations (GROUP BY, SUM, AVG, COUNT) | No | No | +| | Joins | No (`$expand` only for navigation properties) | No | +| | FetchXML | No | No | +| **Table / Metadata Management** | Create table | Yes — `client.tables.create()` | No | +| | Delete table | Yes | No | +| | Get table info | Yes | No | +| | List tables | Yes | No | +| | Add / remove columns | Yes | No | +| | Create 1:N relationship | Yes | No | +| | Create N:N relationship | Yes | No | +| | Create lookup field | Yes | No | +| | Delete relationship | Yes | No | +| | Alternate key management | Yes (create / get / delete) | No | +| **Batch & Transactions** | OData `$batch` multi-request | Yes — `client.batch` | No | +| | Atomic changesets (all-or-nothing) | Yes — `batch.changeset()` | No | +| | Content-ID cross-referencing within changeset | Yes | No | +| | Max operations per batch | 1000 | N/A | +| **DataFrame (pandas)** | `client.dataframe.get()` → `DataFrame` | Yes | No | +| | `client.dataframe.create()` from `DataFrame` | Yes | No | +| | `client.dataframe.update()` from `DataFrame` | Yes | No | +| | `client.dataframe.delete()` from `Series` | Yes | No | +| **File Operations** | File column upload | Yes — `client.files.upload()` (small + chunked) | No | +| | File column download | No | No | +| | File column delete | No | No | +| **Code Generator** | Generate entity classes from Dataverse metadata | No (scaffold exists, not implemented) | Yes — `generator.generate(org_url, entities, credential, output_dir)` | +| | Generates picklist / boolean / intersect types | No | Yes | +| | Auto-fetches Lookup and M2M dependencies | N/A | Yes | +| | Output imports from shared base classes | N/A | Yes — imports from `core` | +| **Annotations** | Formatted values (`OData.Community.Display.V1.FormattedValue`) | Yes — `include_formatted_values()` | No | +| | Custom OData annotation patterns | Yes — `include_annotations(pattern)` | No | +| **Diagnostics** | HTTP request/response logging | Yes — opt-in via `log_config` | No | +| | Automatic header redaction in logs | Yes | No | +| **Change Tracking** | Delta queries / change tracking | No | No | +| **Real-time / Push** | Webhooks, server-sent events | No | No | + +### Reading the table + +**Client SDK** is a full HTTP execution SDK — it handles auth, HTTP, OData serialization, pagination, and bulk operations. Its gap today is the typed entity model (no generated classes, no field descriptors, no type-safe query DSL). + +**Server SDK** is a type-system and code-generation framework with no execution layer. Its strength is the generator and the richness of the typed entity model. It has no way to actually send a request to Dataverse. + +The two SDKs are currently **complementary, not overlapping**: the server SDK produces the types; the client SDK executes the operations. The alignment question is whether the type layer can be shared so that entity classes generated by the server SDK's generator also work as typed arguments to the client SDK's execution layer. + +--- + +## The Four Decisions + +1. **Alignment**: Should the client SDK adopt a typed entity model, and if so, how closely should it align with the server SDK's contract? +2. **Divergence risk**: What happens if both SDKs evolve their entity models independently over time? +3. **Lock-in and GA timing**: If the string model ships at GA and the typed model ships later, how difficult is it for developers to switch — and does the timing of GA matter? +4. **Shared schema library**: Could both SDKs share a common package for entity/field descriptor definitions? Is that feasible with the current layout? + +--- + +## Current State of the Two SDKs + +### Client SDK (`main` branch) + +| Layer | Current state | +|---|---| +| Table/column names | Plain strings everywhere | +| Filter expressions | OData strings (e.g., `"statecode eq 0"`) | +| Record access | `result.data["fieldname"]` dict access | +| Type safety | None — no IDE completion, no compile-time checks | +| Code generator | Not present | + +### Server SDK + +| Layer | Current state | +|---|---| +| Table/column names | Typed Python classes (generated) | +| Filter expressions | Composable `Condition` objects that render to SQL | +| Record access | `account.name` typed attribute access | +| Type safety | Full — IDE completion, Pylance inference | +| Code generator | Implemented (`generator/`) | + +### Key structural differences + +The server SDK's `core/` layer (`entity.py`, `datatypes.py`, `picklist.py`, `boolean.py`, `lookup.py`) is already logically a standalone schema library — it has no HTTP, OData, or Dataverse API dependencies. However, the two SDKs differ in ways that matter for alignment: + +| Concept | Server SDK | Client SDK (main) | Notes | +|---|---|---|---| +| Entity base | `Entity` with `_entity_set`, `_primary_name` | No typed entity model | — | +| Condition rendering | `Condition.to_sql()` — SQL fragments | OData strings | Fundamentally different targets | +| Payload methods | `to_create_payload()` returns `Entity` instance | Not present | Server returns typed objects; client needs HTTP-serializable dicts | +| Boolean type | `BooleanBase` + `BooleanOption` | Not present | Server SDK naming already uses `BooleanBase` | +| Picklist type | `PicklistBase` + `PicklistOption` | Not present | Naming is aligned | +| Generator | Implemented | Not present | Both would need to produce from same base classes | + +The condition rendering difference is the most significant: server-side execution speaks SQL; client-side execution speaks OData. A shared base class can define the operator overloads (`==`, `>=`, etc.) but the rendering backend must be environment-specific. + +--- + +## Decision 1: Should the Client SDK Adopt a Typed Model? + +The prototype demonstrated that the typed model can be added to the client SDK as a purely opt-in, backward-compatible layer — the string-based API is unaffected. The benefit is real: IDE completion, refactor safety, no string duplication across schema, create/update/query consistency. + +The question is not whether to add it, but whether to add it in a way that is aligned with the server SDK or in a way that is independent. + +**Recommendation: Yes, aligned.** Independent implementation is the path to the divergence problem described in Decision 2. + +--- + +## Decision 2: Lock-in and GA Timing + +The string model and the typed model are not equally easy to adopt at any point in time. There is an asymmetry: switching from strings to typed entities requires active code changes, while the reverse (typed to strings) rarely happens. This creates a one-way lock-in dynamic. + +### What developers build on top of the string model + +Once a team adopts the string-based client SDK, they tend to build their own abstractions on top of it: +- Configuration files or constants holding table and column name strings +- Wrapper functions like `def get_accounts(client, select): return client.records.get("account", select=select, ...)` +- Generic utilities parameterized by table name strings +- Automated tooling that generates string-based calls from metadata + +None of these abstractions migrate cleanly to typed entities. A typed model requires that the *entity class itself* be the unit of schema definition — string-based wrappers are structurally incompatible, not just inconvenient. + +### The adoption momentum problem + +GA is not just a version number — it is a signal to developers that this is the stable, recommended way to build. Whatever model is present at GA becomes: +- The pattern in the first blog posts and tutorials +- The model in internal starter templates and onboarding guides +- The shape of the first production codebases + +If the string model is the only model at GA, early adopters build on strings. When typed entities arrive later, those developers face a real refactoring cost to switch — and many will not. The string model becomes entrenched not because it is better, but because it came first. + +This is different from a typical backward-compatible addition (e.g., adding a new method). Adding a new optional parameter does not require existing callers to change anything. Adding typed entities as an *alternative* to strings means developers must actively choose to rebuild what they already have. The later typed entities arrive, the more code already exists that won't be rebuilt. + +### The cost of the switch, concretely + +Given the prototype work, the migration from string model to typed model for an existing codebase involves: +1. Defining (or generating) an entity class for every table the codebase touches +2. Replacing every string-literal table name with the class reference +3. Replacing every string-literal column reference in `select`, `filter`, `orderby` with the typed field or string equivalent on the class +4. Replacing dict-based record access (`result.data["fieldname"]`) with typed attribute access + +Steps 1 and 4 are the expensive ones for large codebases. Step 1 requires the generator to be available. Step 4 requires touching every consumer of query results. Even with the coexistence approach (strings still work), there is no incremental migration path — you either have an entity class for a table or you don't. + +### Implication for GA timing + +The typed model does not need to be the *only* model at GA, but it needs to be present and recommended at GA if it is the intended long-term programming model. A typed model that ships 6–12 months post-GA will face an installed base that has no reason to migrate, regardless of how good the model is. + +**Recommendation:** If the typed entity model is the intended long-term direction, it must ship at GA or within a short post-GA release that is clearly signaled before GA. Shipping the string model as the only model at GA without any typed entity support, and without explicitly communicating that typed entities are coming, risks cementing the string model as the community standard. + +--- + +## Decision 3: Divergence Risk + +If both SDKs independently define their own `Entity`, `Text`, `PicklistBase`, etc., divergence is guaranteed over time: + +- **Field type drift**: a new field type added to one SDK (e.g., a `RichText` or `BigInt` descriptor) will not appear in the other unless both teams coordinate every change. +- **Generated code portability**: if a user generates entity classes using one SDK's generator, those classes will not work with the other SDK. A developer running code both client-side (HTTP calls) and server-side (plugin execution) would maintain two sets of entity classes. +- **Parameter drift**: the prototype already found one divergence (`DateTime.format=` on the server vs `DateTime.date_format=` on the client prototype). Without a shared source of truth, these accumulate. +- **Behavioral drift**: `to_create_payload()` returning a typed `Entity` vs. a plain `dict` — if both SDKs add payload helpers independently, their signatures will diverge and user mental models will break. + +The cost of divergence grows with adoption. Before GA, fixing it is cheap. After GA, it is a breaking change. + +--- + +## Decision 4: Shared Schema Library + +### What could be shared + +The schema definition layer — `Entity`, `_FieldBase`, `Text`, `Integer`, `DateTime`, `PicklistBase`, `PicklistOption`, `BooleanBase`, `BooleanOption`, `Lookup` — has no runtime dependencies. It is pure Python. Both SDKs use it only for: + +1. Defining entity class schemas (descriptor protocol) +2. Schema introspection (`Entity.fields()`, `Picklist.options()`) +3. Operator overloads for building filter/condition expressions + +This layer is a natural candidate for extraction into a standalone package, e.g., `microsoft-dataverse-schema`. + +### What cannot be shared (environment-specific) + +| Layer | Server SDK | Client SDK | +|---|---|---| +| Condition rendering | `.to_sql()` → SQL + params | `.to_odata()` → OData filter string | +| Payload serialization | Return `Entity` instance | Return `dict` for HTTP body | +| Code generator runtime | Calls OData metadata API via `requests` | Same, but different output templates | + +Each SDK would subclass or extend the shared base to add its own rendering layer. The shared package defines `_FieldBase.__eq__` etc. and returns an abstract `Condition` object; each SDK provides the concrete renderer. + +### Is this feasible with the current layout? + +**Technically: yes, with modest refactoring.** The server SDK's `core/` module is already structured as a self-contained library. The changes needed are: + +1. **Extract `core/` from the server SDK** into its own repository/package with its own versioning. +2. **Refactor client SDK**: replace `src/PowerPlatform/Dataverse/models/` (as prototyped) with an import of the shared package. The client SDK extends the shared `Entity` and `_FieldBase` with OData rendering. +3. **Align naming differences**: `_entity_set` vs `_entity_set_name`, `BooleanOption` naming, `DateTime.format` parameter — resolve these before extraction so the shared package has one canonical API. +4. **Shared generator**: the generator backend (metadata fetch, normalization) can remain SDK-specific; but the code generation templates that emit entity classes should produce code that imports from the shared package. + +**Organizationally: this is the harder part.** A shared package requires: +- An owner (which team? both?) +- A release process independent of both SDKs +- A versioning policy so both SDKs can consume updates without coupling their release schedules +- Agreement on the naming discrepancies before they become public API + +The prototype demonstrates that the descriptor design is sound. The risk is not technical; it is governance and coordination. + +--- + +## Recommendation Summary + +| Decision | Recommendation | +|---|---| +| Adopt typed model in client SDK? | **Yes.** The prototype proves it is backward compatible and low-risk to add. | +| Align with server SDK? | **Yes, intentionally.** Independent implementations guarantee divergence and impose a future migration cost on developers who use both SDKs. | +| How to align? | **Extract a shared `microsoft-dataverse-schema` package.** Both SDKs import from it; each adds its own rendering layer (OData vs. SQL) on top. | +| Lock-in / GA timing | **The typed model must be present at GA, or clearly committed with a near-term date.** Shipping the string model as the only model at GA without typed entities risks cementing string-based patterns as the community standard before the typed model arrives. The migration cost only grows after GA. | +| Push GA for this? | **Yes, if the typed model is the intended long-term direction.** The naming alignment fixes (DateTime parameter, BooleanOption naming) are small. The generator is the main scope item — without it, typed entities are incomplete for connecting to existing large schemas. The correct question is not "is a slip worth it?" but "what pattern do we want the first wave of production code to follow?" If the answer is typed entities, they need to be at GA. | +| Worst outcome | Shipping GA with strings only, adding typed entities 6+ months later, and discovering the installed base has no migration incentive — leaving both models in the SDK indefinitely with neither clearly recommended. | + +--- + +## Open Questions for Discussion + +1. **Is the typed entity model the intended long-term programming model for the client SDK?** If yes, it must ship at or very close to GA. If no (strings remain the primary model indefinitely), the alignment work is lower priority. +2. **What is the acceptable GA slip to include typed entities?** The prototype took a few days to build. The generator and naming alignment are the remaining scope. A concrete estimate is needed to make the GA trade-off decision. +3. **Is there a customer scenario today where a user runs the same entity class code on both client and server?** If yes, the shared schema library is urgent. If no, the timeline pressure is lower but the design should still account for it. +4. Who owns the shared schema package? Is it a new repo under the same org, or does it live with one of the SDKs? +5. Are the naming discrepancies (listed above) acceptable breaking changes before GA, or do they need to be carried as deprecated aliases? +6. Should the generator be shared (one tool, two output modes: client vs. server), or remain separate tools that produce compatible output? diff --git a/docs_local/_build/.buildinfo b/docs_local/_build/.buildinfo new file mode 100644 index 00000000..e403187f --- /dev/null +++ b/docs_local/_build/.buildinfo @@ -0,0 +1,4 @@ +# Sphinx build info version 1 +# This file records the configuration used when building these files. When it is not found, a full rebuild will be done. +config: cc4b35bed06ca8e65bca5667af91bb7d +tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/claude_skill/index.doctree b/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/claude_skill/index.doctree new file mode 100644 index 0000000000000000000000000000000000000000..208098ea68df19a5ee959b632c46afe32f21c057 GIT binary patch literal 3639 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 Lm1a=P|IYq@!vrOz literal 0 HcmV?d00001 diff --git a/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/common/constants/index.doctree b/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/common/constants/index.doctree new file mode 100644 index 0000000000000000000000000000000000000000..cd88b8758be35d25d5fd9559db0c47fd5b3bc11d GIT binary patch literal 30710 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 Fz5^(*4-fzV literal 0 HcmV?d00001 diff --git a/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/core/config/index.doctree b/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/core/config/index.doctree new file mode 100644 index 0000000000000000000000000000000000000000..89881c46a9f02d6ac69dc8369a9d041df0e7b6f5 GIT binary patch literal 38957 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 literal 0 HcmV?d00001 diff --git a/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/core/errors/index.doctree b/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/core/errors/index.doctree new file mode 100644 index 0000000000000000000000000000000000000000..ea18a9db93ef24d1b9491fb5b78e1109200d988c GIT binary patch literal 103492 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�dXgib zdi?>^8KHW80MtuK%%NTxv8B|@aX+J8jxt;P>Xid!8YO8Gv_d^X$UUb*m1XEBx(z3S z_%&+Z)IxhoRG1PKrl7)}LaiI)Oi8y@2c4SRZ>)h}ai2=5I8PJLgn-5^Ew1m8(3KE0 zl-kM*UB&{?7$VbpC*Id#A!(@skA#)EF1k%@2BK4^qqN><6|Tp+rsc3b zRP-|TyoM=9Mvw8bsdb^TXA$c;onxG8(ROkn&auF{YS)us@;S!_)ES{3|2NQMB{7E{ zXT+A$W5@lB9y`jMrSv!lkTfXLEz-(y6(Q!FzHo>63Z7p(#5q<`V>eq?kdqv6u2%`x zi{aU=R4L5Y8|_Y|=#C`qxB3MB?2B3&Pmn;AfHGX&V@x0!6~W7csK|%0o>N7hOtol3 zJw!!*n|0N$Cpq$|$S0uA2vy{_K}D3r94e9#TS`S7_cJQuDD!fqBKgpyaglD6){=J+ zlFq3m72|Crwty^NV6tZk>)nMpSa$;*X~*D1O0eqIi1rpLooFrw9bwU8Yd=nKOtLA% z>v5TF>QX%YO+1CU7aFYho}_azdSv+Ze9r2nD9g;J@~sWjlsGsC$=qMkDdQ%4009Z|nE!qh{2l0`~r;(%b8#;l}a4_e;?f^Jey+ArS(p`$CA5I0`eArWaYO`xt%3Qjg{AxS8)r)jw+2ziAUy~p$B zl2tG}-t2Z}n}trfx2?Q(TQVkAt{N?9ZVcFmlY|QkP57~@)o4ITII6C)uUB3U#|tu! zW`Xn_aC=<*?6zv~YfYz7oSlPZF1*|BRc~4h*7vC0&_cb@iH$oCCTk#w?0~9z zdoWpxQxQ!#4|!n$m~BIA$MP$2EoN6{ak*@korwp`05MUaR%o}`h1IRN*=TiUYw?~K z#CcdUUM|$s&t=Jx@xBU3k)P!(3N1^PmEyVXJhZoVZ=qEODME=>Tv&iBV{B>#`N95G z>L|0g4vXSYs+Wv)JG-aOn`&3!`*HS7akg{0P%qWu7K{LPwFpQ49WErVTUo|h=_SXf z?xdQiejBtfS<4sJb?xQF<{4+6b0&1azLTn*9Dz)t#h5CHAFdwrc zsC&s_alMzks0E>Yt2A4Rn*bf4Qvt~)YZa&%wBW)Pd4K;kFomR%wRCp{jxXGnEUn>X z(ABwQIVkpSkP(QpQZj(Fw$SY~U~XDoN)D?4R@aoe3(Z-DXBZWB0V3771+fy2`{C>g z(-ee`zSL~T-BM$A-vUDMhA5_fhp8X%8N<<3G4h>dj$bD+j0 zFgsgUCdY$PEX*qFpct5KHxZ!+;0)CL^-f{mY`HRDuEBp$pry42>fY-1LIE`W&Mugh zHpuKn>^J{8TWi2lIv<|ni{%3J2f)Lv0tlRm6KdblZ32lkl?7CRTBE>~8h{IS4bTJ6 zx+Sm#`=Oz79Cx7TL=j5eF&i%wDz#Y%!_a;+;z!~D;~2K%+HP(b4=at_F<3c((2;-*09glg3)(2bnV{emlB2Av^#Z(ffR~}Q-Rj43 zw8|xReFa|R-zTe|M34Om{8+gJK9=Ff$MEM<@X<}iuq?a}!CK^{53rI4Sg8Zuas+OP zH2_Zb{PLn?Iq$C0^YSur7TF1yh8@9Mq30{FjZQLIR%wG&g2ucwt%i>s_;Eje zJcJ+rj2}P3kJqk&k0O5jB7QuIA75GvA790fZyg06-(w%gz_%Z=4}4h+-#_anYfNM^ zA+IS`0N*%DAb5TSjhHOQN7KB*%Yi%q$qRFhngTm3dUWcdDm`YkI3}sGN=I4kgEMP& zdRrGK6X~|Z9f&Ew#1>&G9CQGVoGmtLHI7!W>nko3*WV7%dUNl3Q1xb^1x7=>;lc?j nOKV-m>Hm5gO&mk^s!F|B>y}WH<`LB6C3GahZepm^otyrDH%}io literal 0 HcmV?d00001 diff --git a/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/core/index.doctree b/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/core/index.doctree new file mode 100644 index 0000000000000000000000000000000000000000..a5c934a712598fcd9f6f75684627d3bc9d24d1cf GIT binary patch literal 4171 zcmc&%TW=$`751(zS(0t}lDH@myAawm^`?>B1YMw;v_OH~hoajWi)7OPg@OS!Lk(GT zF6G6RTLfqyQmg?!C9@9!@_YKb`jMO&Nk))v`w{^HhKGmbIfvi*&fz~gFMoTm;eU3R z6tU4p$rY7~PU1q+%B{n)t><*%4EQhF zUsS=sZb@K{uGoR?N3H#A({^jKFYK1!Ijt_tjMxFuTJo9EaM!uF$rA-v>~<`OQV`<1 zlanlb^%7Gy17o)}N!_SoL-q#SVQ;ccc48NgKbU@^;Kh{8sK_|>VbhOjS*Yonx8v5^ z$AZJ)@v|GvihK%_RV0d-h$%6;ASGX4H*MEVU6iIgPv~MI6+AwDdf&|KzA72d7ZZ{s z^)+_9&Pw3JovUvNe~kZI_`mHuhb@wOf6wezNw}=+TUn79K|dU=d2si>>`#K) z_&c>&_d-|1urv1aWz;U%DXs&T-N)^bRFaadm}?(ChNmN*&m~b>nphhQ<031I9M-B4 z&@K*=7z3MU#AGNaV;VYdciZr@&BDK2##dOX^;1qVl{?0z9wwIAb zLQBo6r}sd&r1W(I_Q>95(Vz48N z0-6#CTU4xTcY+lWj=se7pdK12Fxta)M>7&%x>;D$aag&j?9m*Xt=wp zXyMwE>V+Mm`tTAU&sJ){ecKx(25x`3e6v7Lm@Op4=%*F@$BBs3%?863o`7DookpS+21o#%~%I?>}U+&WJyHxh{&di-H*uU*CY`R?9jm!7m zfA~I5U!S?N)E+nw06xVKAOVjmyC+DV8j@1PJHhxq$iTiPDN-Vdz#M=M&T*Z}O5^I= zR|uXo^EZFsXpLHAZ8;u7*w6w(jBJ_NZs05zOP;I%Y3N8VT8iv_4y^7aCM%;B&$tpV z;DEA3{dSnx_Y$8?4mV3tsi;YcXpuQ69?%7PLK>~E^!SQ(bj1}9P~VRduE3 zh1*S@A!m0H2!+h@NsGC?S}khm)`!M3WDR1_DKZeP6zr%`edJ&cGFw;sf!hMsp_TQF zKyznC0+j85V}Y?|c<7QGSj|&FU+|3U>W>FMv%M=KxJy)+H7scvAw$90d5jA~9yrO2 zz<-yR_J>UCQhjiK4zZe)z7w7lGCi;CvD5UnKr_-bb1Vrs^dKMq^cTOxoebqXzO%iP zEVqBV+1?SROPEV4C!FfJKLhbNXF3x&tgp!oIh?qv)U&6^h%9QUp>-fqr9gyjc9j?#_RxePT{&IY8W23IQTTKl{Bljiy)Y!e%i}g0{#T@W(PQvaWaU9U- zg<~uip{2+Woa1`^~9-(b3xeCDxfMH*b>#6c&8P6VC>XA7=V~MObimo N!!_-=PQ%P5{{ir$Ttxr? literal 0 HcmV?d00001 diff --git a/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/core/log_config/index.doctree b/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/core/log_config/index.doctree new file mode 100644 index 0000000000000000000000000000000000000000..6fcf73d3245ca4ccd42f83db59351eaa16a9cc66 GIT binary patch literal 27189 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 literal 0 HcmV?d00001 diff --git a/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/index.doctree b/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/index.doctree new file mode 100644 index 0000000000000000000000000000000000000000..0d131c1cd6f034ca820bd25ff6fc69675cdf31b2 GIT binary patch literal 3944 zcmb7H-)|%}63%WilbJ~}Np@L4uz?lgM|L3-SRE3}O82t7EsR$DddjNh>Gq6G-0jA8 zCzA)HI|+`Buz11zFZbv1x!v8ZZkv*by;C@mXOIqz`JHCmI3R%~%kvA;j-qM$+(WkQ8~nDD0*o z3%_z~%wA(#>~*%z?ppoi)01yBd_N&GDpC$R?Bq+DOMTLb@u-RMiQsVf=-Gu~GCP6G zIubG_;)E1NlAL$Xoiyi8c$U!RL}>(j@bqRev%5NHJX=mkl2q^3D|K3#4v)azK(Oo{ zQu`ME-^TyDK4`e1cp2)U-N*@7&c2z-q!9G+y^iy(2eNMjjiWzPVEw6aq+o~a{Z*Q# z>;U;^qkyFqHg;=)9eq{If@K{*aHk`xo*Neb84@2XlK&f-zudpk_{NtW7wgKmZbnoosZfqtB7k-v0kOr#+1)DnD=WRyin9k-cCMIU|Fz>_bak{J=N~=% z_#tlZ?p!%)_k08ZpJqsqKt#^&2$C%dvY^O!g7r;MfW56~EL9SLF903f4W98N zrGNvw@gjRixr4LqNqTg&EZFd>RTS>ps=pm%iaXM!^BrI z0?oV|2~f5LW(CIT<-RX+U^QC+`hut2xG(nJvx74txGz+=HO^@sp+dpbS&Rom9eBx% zAbww%_Ely~u0K6KhFDMXa$uZDwK#V6j@K-Gfo5cA>bVhc7(jmX&WHcNn~c>wzGB`> zR_5QW&3mr&1#?Q(gi|vw??66|nMnmM8yYf04JW=A4eS9bB8?hqXab0I?h$%v_w+?( z$TDJl!36$MJsU#$!rRs1*bc$rR|J}vUH z<)yv^ge0M&{fK|+fFAU{1T=7g*2*X~P`wjF>olS%;UdDM?D@OAe82i+0c^!Q5aXp? zr`ehRRIr0UN#Fp05_23!iJ8a{wV?z@7$)scpe2Z&*9-Yoi_ zmGRvH`KA|!--qm1_6z%&{aDzcmzs+lZu+_X*Gu;EWx?+G-@l+wKEfc|51uq7T#wXE z`(=-so?8z4J+3kExEE+n-)-JlTdTVMM%{Ta9{`ESVXPwq=p3cevxbNK4hKvlS~A*zmes}YyL*s3Nk0CC|lF-klS MSG40Njbb+W0~FH~q5uE@ literal 0 HcmV?d00001 diff --git a/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/migration/index.doctree b/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/migration/index.doctree new file mode 100644 index 0000000000000000000000000000000000000000..b564f5b39e610284b8fb8be35d5f032175af4c2f GIT binary patch literal 3559 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 IA|fpQA9K}+8~^|S literal 0 HcmV?d00001 diff --git a/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/batch/index.doctree b/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/batch/index.doctree new file mode 100644 index 0000000000000000000000000000000000000000..8ebda67db0b1edd3540c8674cba50731604f9c76 GIT binary patch literal 44755 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 literal 0 HcmV?d00001 diff --git a/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/filters/index.doctree b/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/filters/index.doctree new file mode 100644 index 0000000000000000000000000000000000000000..8e178a626fe50a57db533590ab62b941d0e30063 GIT binary patch literal 146068 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 BE2{ti literal 0 HcmV?d00001 diff --git a/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/index.doctree b/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/index.doctree new file mode 100644 index 0000000000000000000000000000000000000000..73a9a8ac602b6190daeaab4febea4fc9a8abc3ba GIT binary patch literal 10245 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 literal 0 HcmV?d00001 diff --git a/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/labels/index.doctree b/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/labels/index.doctree new file mode 100644 index 0000000000000000000000000000000000000000..d071310069ea0217f4f3798a3afc91a4e28a3d58 GIT binary patch literal 36905 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 literal 0 HcmV?d00001 diff --git a/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/protocol/index.doctree b/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/protocol/index.doctree new file mode 100644 index 0000000000000000000000000000000000000000..f122ec2c9762e6eab91ff174bf87bc30ee386758 GIT binary patch literal 18057 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 literal 0 HcmV?d00001 diff --git a/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/record/index.doctree b/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/record/index.doctree new file mode 100644 index 0000000000000000000000000000000000000000..13d26ca85d649be13c6a9165e091fe05a605dfab GIT binary patch literal 53127 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%0AprSlf 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 literal 0 HcmV?d00001 diff --git a/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/relationship/index.doctree b/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/relationship/index.doctree new file mode 100644 index 0000000000000000000000000000000000000000..efcfc3a688b4a72e29894e9a861abfee409134e8 GIT binary patch literal 161553 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 U`X3Winv}+psWEa}lC}E(0C74Fv;Y7A literal 0 HcmV?d00001 diff --git a/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/operations/batch/index.doctree b/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/operations/batch/index.doctree new file mode 100644 index 0000000000000000000000000000000000000000..7021845c0c14d84701a7f9323f5d8c5f7b5d09ff GIT binary patch literal 324887 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 PzABd7XG;jp3mE)=@YeZ- literal 0 HcmV?d00001 diff --git a/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/operations/dataframe/index.doctree b/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/operations/dataframe/index.doctree new file mode 100644 index 0000000000000000000000000000000000000000..406df4d6fcc0315cbf8205f086905dec55037ee8 GIT binary patch literal 90271 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 literal 0 HcmV?d00001 diff --git a/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/operations/files/index.doctree b/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/operations/files/index.doctree new file mode 100644 index 0000000000000000000000000000000000000000..a365a4050c079ba3ebbac147455d2a2570ddbbdb GIT binary patch literal 30164 zcmd5_3ydU3dG_Azd++uh_S$fMwU38)&%3kd<9UnkjIn)SjL+v+i1EQ}?@aG@_s!0X zr+e;h!A1tf*efa^=Y~f_#2b<*0tKWalT3SofN7 z??P|xJ-x?!vuvW{o{L)DPR;AF%TS``2aS5iYxdsW^Dkp#kynd@Rx>H@kNBhh*xP%v z$Q+4-xZxRDZ1z;^oYy(kaO2fhXT5TV8@p${PUKZuZLi}}Ls4ZlXaE&z15FG+=$$8I z2B`UDPar=~x2-<9Y%F%y=&R>X`v?3f|7w4ye+~P!+m}v16rp#g-4(B~ z9-tln^!;AD6`i)Iu#iyU_C^4N7fx-&eye#JC`Z*stL8RNyWP0uwgbD~>7?FiO}*2c zdZ&YC-8*0DMCkpsckk$~usu=R51QvIZoMvtdW4rT21ksWe;s=7J0Si+{C_F_znrK5 zygC8z`z#x7yFsVN4zIWB-G=u%$LdYdn6gDtWubHpfAnPBbG!bmf0L%{N&i|58JJ@N zBkZnq+_rCYgB?Br268k>96nHOx8^xuIw$r$C!w74z#Vs21h36(qoQOrpmt0Qxh3h`rV^GzDdaK=&mTu_hw-rbWl9_KM0T@3ezVymK&H7dW& ztW3DCwCWpu2m-&Ioz}U&bgutNYh+ZpMPb4oSZZ%9wN}EVWt*VREoD_i#jbdK`7{^j zS#bBRw%4o&&9&Ul~t(-?gY1NiXZ%sk6dyC0!pj_78htV; ze-4vEZG4ofs+jJ^KbYOf=26v+RoRX2lUPJ2vv}6y?CPVQGS{FAtd+)8yQHr2(aa>cYlQX$T9BS-V(<$*jXn2p zXU@63)r_IFrT;y{`qGnu4fhXSc3HWxh4T^aNlJ5cX268=DJI<7^Bj$!&S8z&8*EkT z%yNa{1kKXU=-fpp6w50$dL|vyLu?q6seWIIY|=F4RHFd%g}B?X=0+#%KA}rA8(7Ni zk;vD2P|P0Y67#o*gL0eAR?KHshDl;49=Sy!Qa$5soNIOJOU^AXiQx(@IX7}*-gx}@ zyrWBTHZ{sP$j9V>&-CC(rdu$14K;0Xct()#*YS&U^c9?B1i9U9 z*5YoTt{GZVu<@4$`?u{0$RA zB~LiBb+AC^lL|rUW6iKZo6*GjdSPFbdNe(CF<;Qmx>UQCM?EM$+}Ed>Xs{a8YzN=!^jCp)+WQGVo+iA-QDqeD`-vI{!lyD?SESGyioJNf+lYfIE%j9s*~2G9 zd(+B{J8pndYgi4zIzjJg(&bMIQ2n}G;~Cz>JA~yjtQtZNdTf%k5hh1i8r3s3HY(>r z{&wFe6`zXaCr^SBZW;?<5rDuB>(GKp7uWYmOND#BeafC17a!ZDo|pPKDp~p z(_Su+HQ~q^I!3VJtpCgN^~0Y^Jp602tcw~O;tsVC_F`cF6OFyb?QOT!Y91C|>(1H0 zb(WW9Sh7{JZJqGw%dYX z=%&#ff}W}${^yk}v%P6ll!Ma2>Aq;jH9Z(} zOs=-a1bn#~c#XOc8Ep-vTEof^&(7eqq-NSCO`cK~HkoLcNgDaXWr-J7sN{k0Ci*1= zgXoxGkiB-L+3odW^qkp*XKv^2_nfn^;5>*WV@M^ualLXZvN7X`Vz*iI6jQ;-%N3w% z6n8KwG|0k~?UmL_ed_MMsdRTwi0-}tzc{<>aApmjqhxbx=BI5DJa&bqx8C;MD2Pl% z_oS$xc)TxNqyCN*$XqrLPYcu^=BQ^E>o;W9D$}N3gyO375*oFq%~dPQsBv{nVnT*3 zq5IHW5!q+hi1XYN7tF~=-_XpkDTBX?T1y6zy;dxcEiJ!$FvnzB3}m9s!l!5~Xr~P` z{q&gYvE3{j+gblh!r8#W-GZ3vB@+dr7pb);iw0Y*Q2Kvtj(d0kb@V_p<-lZcf>>|# zzwSTle}fN`|7QdHT4H2{=D1Fdur#SNY|@Z(O_uaf7HQT4;jINE0Pye_5m_iObG0X> zq{BH;?B!du_EJ$ynbyRRX^l5==XrO%-SCzY_k3KX7)WYvA2V z)2v`K^Nguxo|H=WSX!;luDFp`?RFaTPOanBp?CwgF*i9W%8MQ1im>^!lMenJbZfO% zw;3OtcjRBypg!*$Y(td+Px*#`6dmLVBMG4)E{_>Eu4ch#_p1|opVZW*ryiV9n7EoRvoKs$MaN_-foZs zYOjCE@$K1*ZHlu*cd;%SR)Dbb&ZLVQzQRajpOKy6%d<-k%<{a`@oHG|0*}aVaw2IH z6#k)1Ax4pZ9-4RHx_e8`y={t6a2t=Hjki0;oRh7lhrD3jtJ020UV&h>+C<6fx*ONf z=1Qy8K;;KJT@T*Hao&n>NyDy*_o%>I*@9R62~w#9bSwl2wHJ#3&#bt6bRL&Q)i znvicPBxEiNzKM6a6bn9F3?YvNC4@38xLOP$j|C-!?XuvxLa5I(3r2iA?1{}5gLjcE zxHoOSvY2qnD5TiXO4E$^(L!?O_NB)=UWySvR}3MK5ha8&jQHhZ2ziVsA#9fspDl#? zJTu~-^6@Cih@#^c$%qKZlU`F62O@3|K}pg8ix16nKPaSJF3)|B_pubu{kRxH9?wY# zWq9rv#Srp%PD0o&&+Qp5@?4&0o|^(PrR}fi_(k&E&>_K6huFSkCrV5$S{-Au8CHl) zBB*$myRy>pu&$hnT-IfV%1;3^oiKi$I%Eit;1w^y?yT!)(LaW)>p!Gwsrx-br zM;|d_QD;xxdCGak&4-W7QOrj8DgtvLJd#1fS$3t|r{~QrByIHAP)lzzP%DQ;EAttx zC}t?_+*~ClK5zz|$M{4=&U!M%o608Q%|EnwKSW67t|R1%_kFw*rNn!s7($+SO9*Ae zyHyM!PrM}rOS~sFMFlDQR%mCimjoAFQ}BsG=xSu2CpY^sF)n4OcA*gO+-+D1!Jb$> zrr2xfF@rLnEQXLb@&okY3&jxfMqWV37}{Z4IWT(gM}_T|7+V>ONs*c=TvEW;)+9>c zZR_Q&M6?Vm{B2=3b6G(O(9Ni=7JAswv4jr?DDAFZnrvdHA2XH{AjmD z2U|ADKKzVYJP-_JwXR@y#XV=R7Yl~!ORCXxzj{EQT>?5-HumbuuAY9&`=ZYYM3 zr!*vlGTdG%hLFc862irri(3n!tAT!=%*E@(xRjyV{e^hv>Iey;jJc>5L&zKX0s7D= zhLAV%0zwIMu~FE5nX&yQbO0awOJFSo+O{IewH5=&&ohO+%;f|Lp^UZocrk=LYcW6{ zeyUEJZk7_+}bwUP$nAB!R6sR;?8jH>(BVhDN6At6`>Vwg6S1kw7}>qVxf-Omc) zs(~KJGzbveayEa$hos~x)d1ApKU(NQY(d>J>f`!i2zlybfIiF@L)fAZw)&v`MkQ9m z{6llZ(PB6Qst?h@fz`)W6~ceUuF+=rL;N&DGyAtMlNiX3hcgyUE3(PZq8Wmc^1a_$ zNR~2!aw+ZX1HdM|w5D*dweo$0_kpmVGMdEUbJ)t_=Z9v)AAzFbzu|}8C;kt7$v9QM zy%zo_64*@k4t@AzUYbqY**L8zcd(!8h5tqMud)lM2oACMb%YubK@5k0{14==G_>{q zAV-nTe!2Bu+ZJkFiHe@aI;$`Si zsu$s*jI>1~jhRuAa)M|kY4ko=7CjXx$*4jSC>b6%peBKxQ#c&n@Z#*iPdeZ`K~dCI zoOoR~R$66s9cgruZ_GQhm>}|S$_Hw`x9;*|`A%z;4LL^#1#xp>@0q#OXLx%R@#EgVPg= zSrL(a1j~qs%qVmH7A#_{`1I9If*aZH<>e$UY*~&8w%(I8qm_Rt90o;B>N7<%YM9i0 z1R)`MY?!#!Xi=9DfpbO(`3&%)z%-MsHGcd0@{Cr0deM09fB<)yV;>E(x;g_xOjH3q&NdSb_3vYbrt~<0sphMp>PAgO7jSvbcT1JZQ z;nicd$Yu))VJ}7hDpbf2eM;Vr=vSy-rsy9|wP<5jM)c=7s&+lat9hcoggTc{^p9Y? z6N*`)-$$?v(a$JzpnwbXsHQ6x^X(M{bQ+Lvdqxz6=G+W*Mdch3QRpOB3pul7;pT-^ zOX5=JD$ZTP49fELmKQZ|j2-Vh%??84)?pQMK!-9_5M1 z`%vc+ipZ-WA_>JT5$PjXhKOX8IXo!484ACzIc+ZYOS6>lrM5}ab|AIU-lq#kNKqQR z!wsi~t_|ny+3f$^6Tdlt0BB~`AcWm5)(p!9>@lEbSfl6`trFfs7kBaLN3;>aK}G-o zvN1cM`=>}bR2(|?5g`l!@=OVINNQ3FIxj^*@RTTQVi55rGKIY`vgR9Qhe%n#uW_RE zi8m#0N4)=@>Sc=ecT+9eVp>MLzr#_r>nWzo6YuY%&LtG@qY&?eVwQOK5iCQzGs-;t z%VQlaCTY0EV)7>iWFAmV-cNxcTuk6F^NrTgk_F` zcd9dc_DpiPD2iq8xGe(NjAvNNU8s=ZOj7c8MCm0|FH@9WmTJ+)tc)n#z)`j9Z6!*J zsB;NL=@>*Qq1d?M55wB@(G780<^jBn{JsNt8D;kMabFQkZ80Zlf=O(uE#;_4FzVl1 zI5LXD%0g=Fw1wPdN`cX`F55T7oC0P-Tr_H9GR0_N`J!^}Hq80rliZl0b=mWqGH(Zq2#pu6you=;#qS7TlG0^lb#^gb_P4~0$xWq*|^d?*`Tnyy2#W8xEm6M7#LQ{5yRxS481!!i^T|b!Vei$Jf zQac$bnwA&J4yOX-;Tr059qeQ{UAkkA07_Vg zGF4tGFhQ!7RH{~eBNMjD%2pIaNVy6*gbEpsBPHjkzTIjAg6P?|1MwoND0E*s9N<8z zO&hl|%I`{!tX)q@S)THHDe7!r`H4P|1gkxW6VQGM(JZI1k7gOuu#7TS7qFutm3=j} z2D@r`IfHMt*k7KJYqb3O!f{dbRa#8rp|#jMez`JY;Hshb>A*dl3QCxC@23nAGg`Xu zqI+#=xin(MTJ9HvXt~^`!cT~Okl*6_AlYI;qAqWIpq_*#j{D}m*qLPuK3H7nkgpx}8qo|PQ z{+r=c;<}WKi%j^v%KgWobMMQ?8Dg*E5gDV3?o)UcTjNiM=kSwm5yLO*h;)x*39q`( zxuYD*y3DYn+a4Q3ru*8}I`Ycp<^Dm`N@9P1rqS+KQuyYI8ee?YSZEDt=?Yz-%+m2Q z1%pk}EjL3yz>5`2!>uQezol|$5;QJ@~ zBBTBNd4|@QFN;SK$#t)R3?jL{Wkw!7RpL$}enpXSFOj&ih(bW!)A zC4Z>$&Y*1$`SS%X8PNx}Gop`J1k2Yu|4nhAI{q`!J9dp(VLwL2HBfQo@Ps> zsdCg3!Hc{&#L&fX4v9C+x+LF{AY*%jQG!(ev(gPV7`ZN>BEEV!S)#*g-VEn z6N&O2DV2qw82QA2OIN4U{5Abn0Uig?U7Pifve`C08-}y>{MoRDx_UNjLEj;^K#~!G z5fnJPP9>?+|J0a;$4=oUkc&tUXJs3y1%k$K|CQg665Cm zM(=K1LaNGmb$oWI&qHilE=*OE=g4-c6!9z>8<%W@huQYw2^aBF+bV7q#S;y6gjfFx zo%z(^?QE*mjoaNAHwfdNjg$T~-cMQcn)I+5u0p^EXn;6c-1>))sZOhfk~lCNoW~t& zxcHsF@kZAqpF#Gd{KOr6_}WeP60f+mGn9*|<%ik+RaD1p%rlaoX1nP%xMp{~dY+zl z!)q|p*ft2-{6b}jM1-cU7Ae@QB`p_KNsm|gt&!*nAjt~y-K&#qR|hYq;TUVfJL`d# zJJ_V};<&5)9AWzqB!xq+c;cXo*V06x5jIlyR=R6wZ<_9gq&uC({smGd$gXd!Q<@4s|&AP;MX``V`oZ09=pcQN!4b1sT1J5$OnxY^!5SUjq4}Yx-JX_I)v{X zc&CqDE@Lh09zBQzIP{R4@%A6*Dmt!mMjt!G&fpT~)56KYO72IUoYOdm{ zYmhUj8(cI^78xc@C9xd=7MG^1l%lWGZLeLWx0SGXjhnEMg7>W25I1o%W{hZ5yjXD{f*|+B{3oMY3_Bm7oT|lyDmYh**rL;wWgD|3oAhHhOm8?zKc1w|_u@yFO;B0xYS8nQ zy!0k7xyehxd^?m*4d7j%qcn??XMdHPs|N`6OH)S^@^iMYmIK5*m;e(f3i+j N2TJ8d&PsQs^8c>Gnz;Y~ literal 0 HcmV?d00001 diff --git a/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/operations/index.doctree b/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/operations/index.doctree new file mode 100644 index 0000000000000000000000000000000000000000..c74029a5cf4dcee01d270ba55d6d7b913116496d GIT binary patch literal 4419 zcmd5te8I6K@j9Y0%5=rhVFd>bA-vSD;WZpk^qMHD@>> zIkwayZPOyf8sJ-|K!7|1$nWWo>POB+k`V;!JjH+k@yy}jIfvi*&fz~>|9x<{;s0zu zmQilF(8E;5R2R161PM(|@nv!U^WwAO)OHoQ)G}8QE$jv~BF06mXj*(yunoJdX=J!e z8-3PdZMOYMaf**E!;PRTpX|M7@{+1&f|#jP$?yp=ybD_#`(7-G#czV~w!vb|i?_ z5aM?$B}w_K6X#jI$nCZvGq);OpS{9%*d4aX?%KaUel-42Bcd^xP?2!hVdM8`CiQqN z@<9{%W5MC@;Mu}3nU3MI4uy<}7?a#clJWI@<7VG@nXxfXV>%xyjkr&q-p(g>UuTS` z^C5}j>S9}7XXPr&TVg*$+$=!C@8SRF_najBr)h*Ap4%g1!^1#qr%Y zXz!QGgBQ?c?U5-+&Q94I%RG+Q2`&gA=;A)fOp%POxNhHgw?<3gFC-%o)d3I{7)FC8 zMZx<|o(@Ym9p7wE1t&Q4`|@H-hsbxP%lZBeyUXq)`JFljc>lYyfLP1^>jTDhQ063v zWNHXcwfD8T#AP5s1wMQyCXi}IQvO%kLo7j~tplDK83;M!fJ!h^GSBp*K+#C5Sic#3 znp3rSGazXk7%~x5H);4WxU$UdA8wZ2-b!|Rb#`mI*{=n*_kKPplU>wkf=qmSpPr|0g2pRJY5S%c-X<=vl^ zrMy<9Bm0{h7?=?`$2`AY9sPw>`bAAyPMoYf`idXQx|NW#tRXV`=D=J+${S*YvZh z=?@%&eA6E+FadxurV5l#K@_(A z5fM51YBOBz$lPSP2{Qtyj97nUyEB@)QAjEjW*5;@ZAg+~v7@AfrUK^X1#8=#(u)vB zpW}0{TINb%wTG(@CnP#|U*S#5;bu|TqbZD}($JNU&+VR@;()tw?#8m>x$SEV09go% zbHkv9!8@gd3hE`{B{wXr=Omn0ZKwvPk=<38dXxzTy`aeGZQEl+G|QGfq;rl$8e(!x zJf%|0wqiQTXE?5ZNmPnNLPJrKK+?snMbw4l{Nt%h8%-k_L#wde+)M`#2Abpd-Sv+l zRZ6Omp&JU+0FZ##B2(D?D)~z*EpMf;C)d7QGr|66`=!z4)^?Qr^6g*0jnmh^T-$07 zTm%50W=N1gM1|cGB%S4CMv?Cr>)Rj(`%+0*Ay@>^!8vXoTN&8>*bc#y`upOK90Op3 ztS!e(NE^moh*2#Q+s5cIMS_q^9ic2LwVPFtgv!h9y3SCuyGVpa zW%;6o(Y;D7tO?ks#a?SOBA zv3hyviX2!?XMn!o2{*-`4qmgJ3nI8HRJhg8XcnSE!QUxnQb=~4WI_A;1_TG3U|_1)99LcCs~?*wKnf)(-q7) zRYOkA)SrQToH3IK9M(lgLk-8SLv`#4Dk2FRYG?uowHvL`c@FdfQ|mlre8vR+Q9WBi zy29Jl2|<~EnuAj`a`uv6m&@`c12NtdClSLa9|1gNwIks^o!EVzXTA|8t^|Z6WktJp z+Ia!=pc}@Zfd%FUMyY}7aRjaNkS2tS5N{mE-~RB!>NN`3ifKuVS9X)87w)}hJ0&H7 z0{}`4Q5+>^LPOMyg5u!gA+Q0E8Ppa!1Dd<*3ifDCTD|}whP7$4XggNM;}P<0CoF%T zvTxZp>}&Q_Zo5uu78%@hFRpK|*w9JE280T`6G%Z!qFNL0G-3~oooXHf5N8e(z1TgfD%x?AMm`z- E9|P6E!TdN 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 JR&#dj{{n+8nbQCO literal 0 HcmV?d00001 diff --git a/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/operations/records/index.doctree b/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/operations/records/index.doctree new file mode 100644 index 0000000000000000000000000000000000000000..3a2608c1e6b5fac5bf09160058f832116f324967 GIT binary patch literal 201405 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 literal 0 HcmV?d00001 diff --git a/docs_local/_build/.doctrees/environment.pickle b/docs_local/_build/.doctrees/environment.pickle new file mode 100644 index 0000000000000000000000000000000000000000..4ab681e8237a044f0fdc22fa0b7da7e7ef8c5c40 GIT binary patch literal 2482769 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%0AwQo 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

CVfgkYDDM1GU>Ax>Bxc0(P^FS_nu-Wm2n1> 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 z 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

jLL zQnAz@cv`<>#^kp}8>8aqT)E=RrJd#RNhk>Y9VJ)Kj}9eT_@vs*t=Y^dt(my~^Wa2Q z>As9t#`AcgQhb!ij&j?fg4p%5XYDqN{;QE9H*C7SP=MMQU5d5)A9vRN_c&<(NJ;mf zP?4f%qO;tOdU8Xk5YTsH)chIe&iR}}=R^wJbLVh@`@q!f?mad^S5oud|2Jo} zUvNPy#Tc?=QZU-xs8ZRq(9Wheu$S=uYP2ehY5lU@XU$p$%`!Ufjg^YqC&>`-1}30U zF(%E3%7>RDoRYkN9s@R+CPKo@693`?2CrcILta>&;Z90$I zkQ?T8c=I>4n^(+1VNn#TujRDi++-({7&=9|J?JoKo<@x>wQ}Su9yHI744U<6$hVS9 zh^rB>C2Rq#A$ji)3%u(3pr}KH)(xYUCjQ4WSt++US1Lj5mM`-1gLw=C8ZoeeFyTyK z;=-t@e#uNrUsZ?23R%@%3F1_BUqbg+-3N~#6iFtVEl!M%=V9EYMcQNnQ`>N=2T;j( zD4)#~Sb?BGWTu=$QOGCFg(RGb(Z_MoJPGowSyS0(4h{34FboeOJi?p$v$2hE>lGv>!v zOnLcnGaB?#wVch2p$IcCZdvz?VnsVShCx!wjTJK4+=yDnYGYE3k3MBLc6n{j6`7K887ckWgKhtGVE zo(XT$$A`~+-vQKP0*bE~R7*rQE19KwHVh=J#x$yzVeG?quW0QpSYWDW1PfZbj@sA= zHM^PpBErr_(-TBlBrG7FPd{_u)6a!Ze8qfvagy-)&wHS8emOvypP^S&d+8X3z2{d%n?VR2r@62fhA&?PJJ~f)4}mT}<$l$$!GJk@40{!uLGtZOKh; zE0!=zy0RF~PgZy35QAqZx~-98NM}kn^*eXdfI~OgP&dBfZdw`brdvpg^JXFDP0)rO zIw8RegPT{NU!0jf3Zc%A{(s8%HNtn_nePP$zT2QMzGA+g73KR*;=4mo*mP$q>Vao_ zwmL@`yS19JdiUzF*|TO*L`t!o8L%73G22ye(UbRHwtBqY?lU2O@C0tn4`N#hP6WHX z<&BqeIowY>O@VM1G_YEO)e!)6=LF@-0R-yHfrANB8%4U_@M^{<*Bb zzg_Pe5O|E17HX-G+pdjU+E5sT6CB$LfVoTduuwuQvrTPe&GFV$Z)1>W7n5VEU2Ozb zIrjsb0OWz2$2W{&>wi2&tV@p<``}$2&!pP1FT^`hYJ-ddE#{eask4Ymq84;>4LfGu z>a}c4q4rI%VY2G0_uASKCKLP1{C4kX{Yz*A1RHGg=C#)VY~W$GIo5-)Lze|%L{Zp1 zunzCIO*LOk%e6UPf+Aco8u3L7kJ~zollmNr`zSBi&^oWRqjf(1e_ShCWOQ=^uJc^b z2cLrf-T3UOO5nCkX%sT1fa<-->AA^u`yAYW7J|&WETL=oo@99e*Acm3c2!Ae=GXJa z{}LO3$SJ(Q=YOui{)azFs^S;;e#gXs--{pGubjGcD&^mguX_B6zFD*AuLo)8@NT;Q zVHBsU993sOxR4ph70Oe}SXuIUJoXiO0bf0YtIAO;=?+hAAM?1jiUyn985$O}5LbTs zlt6eR)E7hWHvp=7KVQ-NE2lZZO0Vu-v~E$~y7Sg7?OVBe$)c4j&R^Qyw{p?qr7Nd= zLWkG|_WEz4CY0Cz`4n!jk^Vj$S34$R0QCtVzG6TpHV&v7MkWHIKL$qGbdCSQe=&UF zE9U#r^d0#v(D$^hjDLtue_#6k5nP?QpZ^W|=hamVKZFRUKKH*(A8dW@PmeKuU2gQP zEMm(te^G9bdp*t45n^?ZLh%ZP;uXwv|3GTyXMnZSvfILWnk0eY94v4aHwkBSXd*L| z>!aX=I9Q&7o|Fe{_~5MYZM;go}D?k0kBrb2e6Lbf}7A7NRXXzoGxv2dK1Aewth3#*K9uxP%t zLD95R$~`hL_=+W{(~U2EeVIa`uP@X)l|whQg*9JWhu6kk+lFDo-L^$R-@?r4(UVKg z|0c9vWy_MTjqqES${R^MNAiej9p1P$2P<%62vny`ol6RNgbj46mHNhVDI30VqujVr zO}rbGpr^+sr~GfDN&iv$AhTf8RI2i#F!-s;^BDV7&i@X=1i&&fEb8oCMa%?#^>TWyIs#yal1u;R#Du$OLVXffJF9E~V=TpkI^6+O z#8hBVE1CcmunY{S_-qMhIDm>64h)J72h$1h2mE^+UL7$V_|>c9>D?LI2|KtG)B%5G z7Z`fuL9KBB6)_+f)LBh~VymDwsI?BDtZHWSfkE{)4$5cCfJ|4PZ2#)zW>Z2z$n(%K5))Lr{xP@@>gO|GRB${w(v8()ZsZ+wSG}?u83%HDF0FlK| z_T-r@7{f&~lHCLNT5}7ogFy;5*0E&F)h=%xoIB&W?c;Rfm>fO)HKP8AL+TIS9kEXZ zSg)QAz28f%T(Y9Oy~E3;htddj1rK;xw-o0T^5tRs;?Qg03Na1D+7w*~geH#%0v@Sb z39G%2ye<@#2D6x?EkBwqObq6j^Kh&ZO-2C6GLsZTohG8;)ke%EBB@J=;RB&KK1Pw% z6d+Bp)+IQ%;0f5AnJ_^MbP}7HVp|Rv3k0FT}hp+HEc& zgYwymW@X8Cb8TQ52Vhoea*nwz7BNb`l;z^ zaWYr2MXT5RdNs9RLs+?{0(zY_x%LX~I$cgMLTcI|VqIQ_rrsd>o#u#(qmx8BowgQx zQ1z1)U#0Rdm6vS_%o}9DhY3f!4YULd>cve=CbAOpH&$R^R6~kWL;a%R!Jd`U`c9?$NWaXnNMzP9ULr+HJWs{jG;#S30 z3u7lOSL!T}<+AzB`D_4iXnw2xj-;0BOaT8gr>G4iI*}ASFh@IZW(QC{qW=vVfp zyhydNs)m>FW%Bw%>5572aV0rS8aSXB4j&yK%ITw%F%c(__8Try$d)K4*kROk#0h3S z+nzu?LLk<2AsbP$LdNo9A(nFpb4i5Pt>ASlcynUl#rf!tHt_rn2|l`Zx`VrBnQGy5 zRnKyVIqwngJ{GaCt#;Cz*hQ3?+ti+&-m1pQyC*P?8`o0z-X7` zDg~wz0CNKF$;5fI1(NNjR|}+*45Yd{P|5vNE)>Vh5#jMqHjr0+c~52_{j1a-iO&xI z6x<-t>>yew&`veb{L|FRFirAp43&hJT|x@b4q#8QGh&NLLEdRf6=)X&^1pX+4bfc*Ls( z@k1Ih{&hlZ<^3!{yw)K0`>edDd=_|r9sNlAHa#rx`rC8q+kMY7-{wrW>-`Mw=C9dq zCbxNy+09890ZP<@Y&Ls5n(O`-s0Y489}fh@#gf!Vs90S3>54Ub#*(D4_xLB{=$@h` zG<;zdup-$*DUv;S?7E>A(X|6)Sg_;;i%(bK0TN)?nY){YwW;zjsLbenE^*weZ7=P> zADMXvz7fD!7k=eM;Ry@+kUjTvLy}+ZxevayuJ4|074B8OVvCNi6q`~BzWZBAQ0n;Z zBjyRe`Zjv5L1XcDdM0En`teSd_d9@!*e48%?Gr<9yg%a(hgU~z6Mprbjnk{$9cY=bZ;wm|}tQJJq#v6Ckdhj9_Kiu?!c z2=tKTBje$mE&PP!S0oQ(X|jQXAI4oPc8q^Fp$@OG{doF5;Xq%++F|uQr~0uJIa{0?_!GbC181`&hP#SiPSZ!whoPsdOjFJfdm)Enm!LyD>W z96cAHHnb?*5x+;r)>3$!$AjXjs*duB7&i>+i%o!H^Th=ePn~r@MeG;`b$=6}*g^?| ziWgRz%IkGs#8hEWY^u0-pY|`1Ayawr+)Qbx+<~L?TM&gegpe0&pZH%RJWj)mrssOd zBNhoGXN#mMWx{-=SC5gOOfD_^}HI`yv-5G;-VB=Ijzp* z^C1S>&gkzqKTM2a9GlcGD=3zfQVYPrhoJs7#)W{0$IR%Yr~L<L7G;K+7STe0tJ(VPcHWT?r1ZdJ0&`4$!X<`b{*2tcNjDM6^L;>EKDdp+u zG7{Hxf&&T5b3`Fi;I5cP@DZw%8k%};J4M@Act@*F&C!bRXB>#Lu ze`4QQdP!XHaCz8sm)!iokMa=q2f<0j>2j{Hx!vnr z$kJ)S5@6kF7q?O)t#}b`P1ALU*J>LuKiF?BRa~K0oW{IRofm4}c6GJ@Wl0QFb2Vd; zy_Hx%cp@b)qgLoY!nJ^2lOD>Arxax1Y;Cuj8R|w4+s1}Dfd}lD*r>01OyaNe2;EoO z3}N#)NVJ8N8d8j~q>4U*ZZb>k>~Jo-g@&~vg%VgHx*+fxEhj1LJ%?pW+FVCl+LOfz zWYgM)9gSQ8yT&j8hv-Z(X~LXUgrhhL$gH6*E@ST_rL;m<^xf3sNZ_qe5od^Z5F&Md zBoKjq&vI~9+URAiu=VXCuVGiB>R1qh^Q8#)&gra3-Kh^sfhBeZI^j#iTc?Lx|@*2jt9aww$6OX8P& z0S&REgRjV)u>O0`ll*b~h{_#@43|||vDA!0HzC9U^BsnVK6}IVL1#I$8L9+Qkfq24 zo;b<%U)qfPLHst!N@tGB=y$ak*_Q#Mq(aTgI?Ei6S7Lx*>P5?u<1}-;bMi=K#{%u8 zNTNqzR6snSd^)|&1sns)0|)Owa8!k!xXL`VYRwXwnP#%U=7`dT#2YnoNCHz@+L?c8 z&GMAY0xI~2#$4dPG_)^_5v$qcyi1iT#8WyUUi26XALEICy~?1+56GK?`Rd}I!lD#0 zz5|n~R`U=#6em-_O>lm}y3#~0{=xC$SZcmGq7xvKF4MW~x$FcPeeG8Nt8*lZhZ~f7 zx2IyxrPEmIgb|BTpW&f`2o0XZ1CgHfC zXeB$SQkZ(Yvq?<%p~mk$JM* zhs@nm0*MLeS@1ZlQuBQ!(hYaRS>%C zu*URVHM|VHI<`=CI?wZfzL}td2epnYRrctG0jOt&1I^4(ncv8~l2)(KerY}SwVyMx6Jj0TW8(PcE1sLmc(79Zb` z>RZb`d^w*+%L;@K(0KB1r_(G)~?h*_qH9>!xJ!D}R9~BrMs}DoPNH8}(E=XS83=&%LhJa{od5;5# zd+8nq@u}&7z+vH`5D5P>^g`~G&n5z~y40$I`wS3&A}~6XD)29&Sm*!dqez#b7`lNOjk zz^v&p2%?+zj9_(>2o?*1?+Jp76A-MeiJ-Sh1a?;cu^_lKfFPi%&8%=vlTF!K{Yh!+ z^4d+E*JM*CPy;M-zmcY{v`vLYZlhtW?(pF?{w8YoDLhJ3V@T%wr|E^<=@&E!u3Nx8 zBjB#G;EbipivMrYTBga?+B1d~yL|*#Y!F|Q(Apk!2h%p#WNY`zA^N?fwbv!Iwzsr4 z)MRTfrZ;dm?<=kCw5^$L#>C{K1-5g{px#1F7Q)|hv{6qm(x}Hjk_Z~1n_r41s>R{u zLc}QDL}`LCX(t33?TnfaR!Q0&e@{qxgC!*-6mz7_3j=K?s^pLnb2L30F=A?;5PoGt zQ)g_{?6Di#cp;*zG0>p{{(DUZ%gQqSxpey&*2(HHl!Vmkn961GYd(uQdnagOG2kmU ze@<=;_2TpT)+}1SwD0Vm-lgk8({0?rcyo|BCh+QD(dc}|!|ieE$ME{|Ww=d8CLT&p zaOl@cwZ`3$Z0Q*j>u`F8>0HC~nnDHmoO``0N<>lCIVYhrr4U^;piU{a}_;xL`9FrBS1rD|fjQ83LFO!aalge~-7 z*RfZ10EgPE{UqloQ0FL69W_B+E1=Q>s`<`?PZdKr4%4{`)42-MyqcKaA()O6O!cx{ zBr^(bt~fyJ6`=JB&?{>Kdba>NQ2^D;!;%b$e1&~wq>71yb)JHCo`Uu2ny_vbSX~0E zd0oKeIpQFluOOYTAe~YZ(scr9p+H*Mv@X!cUHiB`81J-%lf!6bwLt;gpa7m01JKT~ z*4kcT0QzrCm}8gG9jujSsTP!BBmucqvu$Fm`Wf^lR*`2)t1Ia$JR_{eS9(TRL%)XG z)@y(N^Kh%mtYax#zs0oCD{WkxK-oFfZzq$l9%cRm?Jv1aC_~P@WM4+XcZkL2yF?f{SJZ!4-nw zGC}aq2?(y75d_xda*ZH(X99v9GlF1;@Sq|H-kpHp+8IIcWdf`6J31a|x8 zMnUl21O)G#5d=5WTUqnJM-aR(0m1ub1cCML+%5=iO+fI$89{KX$iasN!R-kMJ~|@^ ztlQ}mg5U!Q2=1K`1a}G#J}U@5lz`x0X9U3~1i=>t!JP>R9+(jXU!k}1Sa?Vf+?9af z>obDjHB?8F5qwh+d^7>Uqceiwe(8np3xc~75d2_95PVe-{8$itA_2iqX9R)W1pS2| z_+$ctU(E=D-_cuH4t_5PK9zvr4>N+`&w}7Dg5Wa=2>v=F2p$(Xm?bXg&m|z3O?QM> zBfDU|U^j*K5(NL6fMD+#LGUZ#!HWgK=Mxa@HzNrCAP5c;1Yb-*aL9}xIE>!PW8rW? zaDM`VBW47_QG(!TLGWM#g7z6f&?yMgg5b*u28(5#RtbV{Bp_HbBM8p{ux0qCrD{ z$1myQxAgG``uGcd{GC2#e-R&h(Z@dYu^)XLOdl_&k0a>gX!m+g)Vln3_is(mr*ezc4I=hI{S;{OPAnvQb$5q) zCf!Sf>-!0$dik)!TNoioeKFf~{sHu6X5gekHK{!E2h#V6+lL1WvX|}>WHFm{{>uc} zMGDzP3fZAGksT(;4i{w8=*GUdEj!;6XcsGJ7b|E-)Py!ipdBgD8r%8_cV~2y%s*OS zU7}!JqF}Yfz_LToZUwg+SpE~yA*d3%JViT2KZ_lPep(v8Of`Ou8i_x59Elc?-3LBS z06iH6)Z_n>zWUF2Fs~9YuTx;&q`>^DVK67tn^-NJB4B=P!I)4L{~Z+3@mqt&M63Lc z?)*J{(8>1dD3m3F^UVt94GQN|^oWD{vP`r4ugeAJf6-fX@dT|%dHeB?_`X_^`YQ#@ zKPxcrQDB~K7|fWsY}N>vKLx-9g46t7Y4sM>>fNf=qcUjWraVB3hrxG*tXkU1?T14N48fl@kLVT-fg${M{baf9) z%c{@q=3-q`Ue*_U?_xR~+J{PX`Ekl@#uD<^fPCsO`wRWQIbT-fd|8pRg2_316vIzt?ykd2g}R(x#s%|WCg3O=rNP})pB-W_ z#f}j%f0z@?>%gcdgt69CpijS|nDCW``}7^sr*UGbl%?*?X^W+KTYNu0q)2$EVG_hT ztQ-_0LTu}CAJj`^Sho+NW@BX!$`_!u>Q@!~uPXTam@M}jFfY~@rCX&JULHe&OlG|gjDd9yC+D^ZiABF6V7m+_`7$S>29drZQM2ZV5c$n6; z(bVNFs>3@~cP02l)bMf<)2>n`PXmY204*(*ilr`Z#b)I8vhM{gI{i(?i?#sdk?beU zaTfZ9;HHSF5i+l;o5Yv+Y@d#r1x@*Nt4Wuz;jBymR8YN|t8>#~OIR7%i1ZDmw_ zAJUDKIgMKJ1}43fNr@y{c@UaaD^{Dm1*(%Cpd6!;(ZwKi(5^}gu6o{Xs(1F)y|Yh~ zy(5mR%7N9EikQ9ri)_c39$;VAzNTM7=Q}I+uFQ{6)<4SJR~$!Gg3*bQ0Star{m@xH zf?P$`>@IIrW_x~QVkC4YC1*7=@*pfQO?z$gTq{Vb7+Jk zHz3WQm@DTS|Hi5VG=C4*{C!E2{FQSMl|!rf8!(~SwAinj%LmX`2gY)F{UK~tC^iQf ztk_1WU#J0a8pYSfk(ROS4Hvgjro>5;c!^C6HKRjELnL#V>@YudEGKDc*@}ct)QkQ7 zYq-#PmxruuNCr2O!Lb$OcfpYiXvyJRCZx>i>n^V+(C1-DyR;)UhJ=3fbU$|Lemtnj zew2eFah{^UV#Gf<(1!s>>-u-0vnVrVjw*{`f`ic0aDcUz9VQ*6QZONdqN-7bVZvE@ z(Re;on$LHco zGu@#aJ?bSkCziG^R~@S*<}@uahcqcMe-sOIZuQ7F>$IqESkHqmOAw;lW?LbWM?K|M zj`pLKobb@qO?O%eC@*N`MMZiE61GjiD?6F)Mh-zNG1BMEPcJj>YgVDiQ`(|sucV|o zN&1v5&=Ns;zu}rMK}K6jrbeMUodn-~adfCm_4Qy>YFely9|p9Bsxr4qxhPeJu*{2( z>~JxkMYg-iT>041Vh6kbCy@9O{&jtgft4vEk*%7%%Sed2rj#E+UT>9%Rr3xgv6w1u zfk4xvNC-*VQwj;6fEu&c2VX>n3sAyQ)#I-%raPc zc^ARqqZR;|cPxM6yU{Z$*!GvVB{!*4K5DhGp;bn}Y3^OQsi_f5tWwH~Xlo!Fm*hjM zTJZ_^)?ijHdwFn zA*^A|R3-ij<#q@(U?e**Q}yPspp7#ZVswr*8@#iTj2=0a=W5-e^$Kt51={O?k*wV( zlOEFaV~Zf4ohW3mLp6#NrL0X_koXiCCQ;wY*xmLwtSjX{Jk9sf?4_Gad)=zMCmEc) z?$VIpC&6;nzNhSFct)3pb3DV+$m_T*-U)=tX(+uNOJmhw>SN`V*Sn3?BPwl1Q-Gut zn2#)V48;O1jv`05){5}*WXSTHF50-p`JHBH>3>@jO7SWJwe*?(Yzy)`WuM_2d%lYyAR?LFRS=hqP!gd-i#SUCdR4{>po%~P9Udp@ z=$iPP)_2vztQR@RQIk^j6Src#ED25)%olV7Kh+AYPKE*Upd-WmczzILho7J0^(Zc> z8M_tA>RP_VNVxVd8zN%TldO3-Vc~^aCAj z^!~Q?#0SxtsT11mn7TxdskiGfb##+sYQftvBq%B*ZvDjCPn*7Nfgu%KKfNC7Ck}ql zfgaHRm~)}*%bckiyRBTS1u68Agc^SJj0hKZyULl;AGjC!tn;d@09iCNba(|-Y@rD( z;ADpzeGw*rkcYE?^%iV9a?l5SoYS(ed0r9v7apLZZ zE$NzUu?b@A_QcH}G6=T_!ecE$!wK37^GBuKE>(RB~DBiJ5hz#DRap9%^{+K7 zII}7q%D?bSft(Z3cq4zOd3U9W)G@3Fv3pXT^HHbwh>0rFmHM^=l@&OFWh0CHiXdw& z*f}`6rsJZA{5J`=LeWG1we&shsMoiC~(KW(GjFX?fCB$kkjdMW!8i-zFRPcB6Iwk z95G5*j{hFP*jUhZn324r5$x$(1Yjuk#($q9z+34)j&{9G05%pZ7zQXabeNCt7nq@# z8vg^1FeRAAze8X)kx0uZo<6o3!Vqa6E+%5e|F9!OiJ9>~DiG_HV7`z-3c^{t1!9af z69#-YHNznVp+Fe_=NO0G>|QD`GfiCMcnw5}2U?7yr{WVOmo{B3t~=8kqh` z(Wx~M!*X)$4C{YhV1xrOPN_f1`r%2(Lc5>d$%E$sfwmwDO~+fD8V~Csffd=VK5ZJf zWXsxrSa2<}xWZf0OAIixG0LY7y6N`Vt!e)|26ELV?$T*smW^EhF~PjdVm9D-AM11j zIkao*|44vEcRyE51DNc9`acz5XH)~@jn0+vz&RD7b*f?R@-Jy*BV)DBz;bj1AKOCk_pNUjer<04^v!`wN2T zg82eh1m?iagntPvc>gl`xPm@jLmyYu$7|_h2YtMrJ}UI_Ci-|YeY}l6-bEj`(#IY2 zaW{S3OCO)3k1x^3L-;U_>4MdznJ-u!p2)|Lqx0Lew0)F5u7Kxf(^RVR!l0!zRoR>W zRQ`ym$_s*5)UuYNKc`1s0X8vG#;w_iC$hj{PD zWsg6B;-&rpbk_KLM9jNEjQ@T9Vt&~fAys62kB~*G#Kurv}p&lk(xJ*mTTTji?>o1$Di+v_IL-hHe=KcBwCDi%d~kbm390RozcG90jBV6#yBQm6bKoO%8q(YCsz?qCw9mt4O@L{GGBV<$-s4WWK zDBqkfAs$SrFW%tp@2BHH@ciddOBNduDn5#$6muhE2+V=!z19p6GkB#wkiXcO)@}z{ zt*e;p2QjS|MrqxuX{9}Ht)%1YyV3rk4#-9|Y6QsISWViTLn%10fGUx4ome`~U^gZ% zQ+IIfO;s$eVJ1mgBY$}{lXP|Nw_xgCLG>$9b=KQf%8XHjWu}BkJ_`3Q-~=QE^qDP9 zH3`XPE9g_Kz-B9Ag}dm~MExlJuz?b^e@u`|e@luBYf@N0A1ki=YFljElr~E%tgAtf zipfe7F%;jwIFqGn>w^f19zn=Np6cy-*mR;0AllAS(g|s$ea8GKf*UEKg6jA3MivTr zQ@PMl4noy|&{_&Oikd2Kf*(dOID(!eg+oixzqIdUe(hooZ-}sWlO7K{^ogvWlR0%4 z*;r5FYNyXdW)Gn*)g!}9vGHcn3J1C8I#oRH^A*ee8T6c*22y^!Nzvo*YJL~LnhR3# zpR4VwUCbh``xB`@WoV8Ewx&KXQ$K-AR57Zvnm`pPM^ziu+WM%X6{{FnZ{xs%<*90e zTIT>NQo)Kro!vO7Xt^o_EZ*2U#{rm~V!5If1DlBG>pJU;!0b}li-HhoXIq=u%S=rb ze*=Np1JgKW+uE==6gpHt#=I*<#pSWveNBx3V{LUd0}1hW84?OU}s09gy2q z5Lf474I`AY+)ZRIdKiuK!g93y;E9j52u zEQQAWYn(aoItLEeoQqtDj5%;slmq)|4p7V_#cQKbd&5R)Mux}hoe8Ko5MWdFacwgu zfNPrthDYUqAOR{gy$KSeAew)zGYQu@kYF=!a=|nv;dRkI*gr@Die!&8(+NY;2~*A# z{F4I(FQ$rOZ0m6mHl~1!umw6{{jXrjF2MnYailb=T3IM_nNLlqaJ5BiR**$*&JFeA zyu}!u9MVUB=5jol36*A{^!OdL&SW{IXtUh*@e-mRIgT5Vp!!)Vr-{eac*eMuF>B(i zITWSKTgQu|au$ctGkn0pSn>LIW4^;~=r@lV$bE#N%Ug?6AZPTh?%_~93s8}0?lrG9 zf9q$>>jx1~7kEFNKI_X5c6n!xA~;&FD7@uoujo#DYY_NH!J=>v^QBsV^}DNx$|B8B zyskq!U2}oL302-2N`27bLGLQm%mX|DJ=xZ#=U}P7=ilrs-|uyh??`1nmhU%2N9ZCw zLJ`hi$Y4E5{f4#V*d(o_mHUGjsH8KM+ny~<;Lt2(^x|N2MB+EBRHgQx|1Z__>XrUn zxORJSViIwjlby5(9ppmvI969S>roY`Nq9RcX4)IjD`lWpE9HEF&QZ0roC`-mIWS0# z#44Ip$S6t8L>6(Y6p77&y%57ZybL_dN?YmJRDVC7#Buu#t(Z+(H_=!|m^frtA7xPC z+_~*oxl(Mjrw$=}H|m z8`g@Nj}4rgYSfll&bszTCpt9sn*0f15IPIWgzH2%ds~Y%J6A&PLTy0 zTKOQvCUtm9olR12LuKlAeV_vW%n8bwk{+ff_=LQRADhD?Qc1WLw^T&Z>7WyNBx`zL zR?<+5cwpjp%5HN|E%%U%n5Ry@V%2gxJr|fd>#StiP4a$vCcIk~59$LBpt7`o&Y<{; zL4B}sP|6umZ~N{;4uH6jCIjLt2E-*b6_=9yRCm>XwQ&a>PTSi2-j6gw=#IcXrO$OT zjh7(aLG{u4h@u5G8Pvy`2E~OkYlFJm0hG<8$`v&k)W@3!#g#H^gZhL6s7Ogo26a!< zpgKqdYJ>Wu11KBm!6h{r)V)oEnpYpxryM}ph$^nB$)G;nG$<~YS({UzaR6nbskp8t zgZgaKptx9OZBU7xRxe&-y5S=ao0NJcrY7)WV7hALBp_f$Y9cEa$%Cd>Vbt)fK|FIgU_%zyu**~ zh6Mb#+D)X&SNdNf=uR%%uQ(7HDXYmuUK=Iy71-u0A1|d~&TQ`BRb#}k zZ-A1oIaBhm10~jT%jG$llIxee0GvLg*O75Cgi)$gnZwDkVt`0Cgl1kA;w=ty|7>bF9n*0{{v_ETw@nqDMh^}s^}y~LUKkXECQRDlf+fbRq;&1^<9uLyNf z+J^6%X2nmOS;2)i>aZeG=9F1+LzET4o?JN1o_x|7K9>lo13ywDmEm)Z)WE`{@^al> zShwvmt@dkYGPuY|9Wv}rAQxI?GPuxckPPF%p1xLl${GA0=*~LeBQ;tX{JWzfwEyY3 z0!3%=$=-Z+OQHDz@F!>Ve{nz`sqf0@Z>~ab20T>R!s%(UzdEDmayWH(Z^Jfk6qD+` zQS_&-Kn9_Frhpt^JSF1%bZ;()9ASmA6gi8O4r}MWflpxqF(EV>=>8O`EqvxVrbfAqv(T@bHp?y=M~QI zkDxp2z>gICW%zeR;h%2y1hHRN;(Qm_9vsbW)0+oTjl0Wr?UBwTbMdq~Bu6R?Gsz!~ zk{pz`OQt7p?at_rap%pQZ z?&&GnmCht^-OM^9M2b#xpL{+_!n|%2m7;9G#m#+v@MU!_#Igd55QGwR(_ZsPZlqX( zzZ91PlSs`OiXCWRu$<*g8dn3YLt3PoHIw$mC~3x26cnfoyuw;w7sv45|P1V~9Y?ZXq#9x2%9*4;Lp8ETC;A za<6wNkGa~7AD7XLVMxM6mdl^eVmj);0VzT&Hy`Qjt_>mG$5WEoO_r{)Yj?Yqex}& zE;1Jy=N^7A(!-U`?sT?*K2d{?q8q@7wHR(&5x0i(92|S6Lb8@Z=7*Z?)}k|=TnM@joi@yPsZ84sN9jDdJ57dPqh!)E z%~Cn;OeI&ku0v&{d_4E&H=a#^)z7=KVTZ~!`x(-s{g)v$YPnf5opP=u!&hZKC(PI8xLosz# z#UfU&Shm_rVL~78%#U^;AP;9*J28_{>RGM_!P`+~;Ztx++iqGlD~FdkGw%wzvkvn% z3iCDz^B#>dZ|90pq;co9mWuy`^2nfaEt!}Bi3v!S4h^&_)o$ngztF^2ffLtXH46qA z{&Vw|bA9?c%o`Nu<%D_PjWBP03axghC&mzxnM-Y0+Tm@QJ9in6W^FK>%|F3b=b-cv zoUes`K9-lebmDd?kM3o#=QiUShLVm!=dUR=BI6ldNQY~S7|>ubVv377q#{<+9oha% z^<7=Z)G24}IuH!guJJPf|XI6Xtq3X%$0TfId)PUC%Y0~>-bod9CSWO2^!F~@3)>k9=h>kSH{VG+ ze38)F_`}WDI`sK0iDKm|o{pcfeV&x_<*g2{wws5~r$+eI|6yP4a=1AVzf{@B8S~Vk%gE6l*xHmrD^&Jj?ULyUp zp8$GiBY;%0ApZb*nnIxc1L?{HxWxl|mjkea1=vdk*t?qm_A&u>XnkNeIRLXMSq~Rr zH#Y&y6JSTw2lgHZU~}j-EL%qku=h3r>}Ubj76cX_t9Xk8Fq=5CU4T6i8L*YhdK2oJ z%E>a778Yt4x>6m9*@d-6gA--7f(+@4xw-;SP+$C9^u_JO5U22g4>+*SMt&bBta~!b zx`}06E}R!5!X$vCtQd&+#O$FNrSlZSgVyD#V{oXtAj#$NC}t%pH;zLuY6(-TI;%U! zUL0%}jq3wrzY?B(*qLV^ap0Lvx_OH5?AKAA-N3aI$#kyCvD%dy!6$Vr$`)YMMH%fE zv{vcwCr_bDfzM0BUBlO-3_9?%BTxjg%cI)TFi1I;S9AL*;r1t;x&0{zZrkAY<-+a% zigJ5Ym2nhSYwO2KCLcwLtKArQgbwi0d8}P2nk~9v9G}yCdRq8&pEIBS&4Ev2cNp!w zM)>rnD4#A}0+W%>>%oUD15~j9=3sde;k4U{X@&*r9(98`gvX}{QE~`1Q&PJ8$^r<= z{0@8^cd{Pt3)1OyJC#W?H5nj6Ayc}h4m7C&{vy2kVl}Vy5y*RY=Od7EDpA*mp%n`} z_s~B=UQB%&(7K40Ib~C?2@5&ICvc^i28zqT#8$;+q&y8buTSE}l+T$|T7h&S+!4Ie zLd$`I9zoo;Ta+@e_GD7onrOit_~=cFA_TCbQJz|=4))FQma2L`i}cYwF7cuq2;uTO zd>Ri25upQ8>KTXXFjX~SP&7NsXT}GMqv1L)X{FI!%fh7Djng+Ui3j9`L5;P5^kb-G zTUytTQH66jeH9U-S4#Z1T6`hitQFCJR0cDQ5%t@!8H_0~rA{@5UeCqdxL6OkRQ*dX zI|1JU7q3Z9lBU;9*OBNut()$5P!qOj1YhyuW-gi4YJrfnZu*MDtL?h!A-bDi{nv;F zs=4;qX~^VUnJxCQU{nVT=8?dn}Iz^R}BLD1wF&S zeo0rgf&C}lQU{n_%KeJ&W?;Xjs|JDnhMr+yzoo0%z6SVivsEYlL3cC9W>Mnq206AHJ;T6urz-<2-d!-8Zm9#zx(oK8yA?1y z1+sr{{bFnaBzX*AJi|aPE8pis5A^ zr+SH8?Hp+_(n5}09FWRW!L><5BP@Z{io)^qCtMLDaN_2vN+8waMIpt^;2@V!q*#+( z=$(ibCp7lZSyZ+}7b*#uL1DuOX52Uop|N3vQ_OG0Ulag{6q_)YYOUeJv;97CUXR(- zE9@n0p`MCN`0*0(LI(-BU$o3VA_03uCEz|REKt$hqZ;T)^jhYGLCP*A>s?$KQU%i{ zKDsQF&Yjb}a!6#FlAjjJ+PW>Lm$6?32DE-ze+xP9#}T1J>yWO^6QkMssaF-GG6Fyf zl(VCy!N*X`ni8n4GJ+N=9|$2-G94%$I9|koG1@P}c2KxVpjw_--XPUk9p35OBf@|# zFUk_T7R6}f(}a{%2b*S;uQfjkKGBMuP7{{xal$T&jN;0IhOtOftg`ZEs#*Z?OSfle zC&iSiW{+U)E~N9P$o&iQ3Xv5=!jRz=lVmv2CE-(eJY_3lo0{<6C0K~W{4yU426+Wt z1S`wSpz{`b^HJs=h3*>}e@lYhR-5)j@zrMfUNUO;cOJC|I*eNDcRNT%?F%BKw(=bs zvSoA)jUc_d-%?Yj(o&t(EOH04Cf83#H7q9rHkzIsD0Cmi^`*Au5jqB)EvlV@;*vaf z7&QUsokl1Qmt-M#SEUWP?e>w*I^4OlJcrJ*Zo4C-vtAVKtb3K)j=ZGfD2!e3MkhuF zpji;5;O@cGyPDJ^%5CMPMi8D!9kOtNH=nw~xU-VEYgEWN6eVFZdSmO97XI*!L46b` ziPbo=M0@kYfiycK8=m2rt=a|n65)B9Gtb){cy3c}%oU#R7vcH(L)fqxXS*cGZuWk9 zbQYD8T0{7kAX9d!2>m~dNJ8usPlCPbCaNZ@SPBaT@|;5;C_i=VKxRBU+_E*F;n(-~ zuPNm==dc-2r1Vzlkqq2RLph3d21kc;8IHX%BZa<3CFXzxgdy$HZq*o?@6{J5xYnguX&l5g+C#qTe~kT?>YTBwsXx#TINQ@d&o4oQd=e z5BL@FRJ6c3SJIKTYatu;fyt>Aq%9IhDtBi;;UbIG4Y3G%p;T;(p zg<^3FWSbsEl21~&B`S3=jy#2UYRLpbK}WNb=80_x&cb;E{bhM_WB@uD_X)cwKO>A^ zJIL>DWqZTpKNPmN8iG{^>Pte)oHm7AIxyu>Qi zD$&-MXI@#!Rm;@CNDdp)`hHW1Q+U;5%BG z-iow>C(<04sBr9yJtmlK>gpM5U3h2XAR;y^=W2bTbqOE#2(-ufz7-st(}696(U_em zWM~i3BmX?QOg<#M*ef^%H2g@WG)ZNNaOyBWh%v>_&+&Rx7=pS{+0EEX z#^!xF)Ichs1-?lHA61xmH+m`mBEIS;*FhoNT+tFDuw*A^XmugA)a9q4K`MNnhC1gW_)BTwZET%VfLj_s)QBu+a%s8W1Gd& zwn1Y(FuK`ii-*;?=Rr<)kk^0@Oyhg~0=?9vUt`q13WcL2PF-pQDn7dJM;KeFU1Vo{ zwe08!q&v~YYmgDj+YH|k7T(}zuCzIm#TQVAJp0lbT=fm<1!!FF$5%|Drii!V*wyI# z(~AYH!pRh%pDEorrJ5op8Yj1QdSeGDz)R4M$o~?YDIL(C%tpk()ZiT+2=Gm*1odz% z4};8&n#BG6T*M9SGJ*PtLa#p5OhN`ftTK=}-Y6v1JRovNF-{CZcoQ<2^9hA=M~k^H zaSJ7rFPd$X3_aS%EbC(wxl(!&eaAz`G}1zi@U5nmdfq#(#r7a=S~`W@obRA3&XpY} zzWT{*yu8oqilnfc3mjhk-K+5~4$HWJ8sS%W*jKCV%p0CZ765xAtZRoQOfR3ct0Yzv zCH95h&Qgn$G;@6|pF8PqVTC{v%^p8m6{{M0X|~99RYARg>f?KqG!!xyO_CR~lfG-= zGgUgqm{;n0?+1Q6HXx<^g{p=@Dlpjh-Zq@J(h*_om6W3}EHo0}#cB%&VxTj7$1&{4 zRY1#z;Je_D;EIl;SWTN_`p~AhO9lX0{MZ`{>UbK%q@#)HBjnH!1d3_#hjA8-!`Y0} ziXf7Y?3po7ql^Y6k9ze7WE*N1XHM}GwlUc6-9qXb{)>SeM4u4KLbbm?a6Z(ta}B#( z*zHZ8Zgp-H=lzRxbPapCO>>MR7NKOxAPvl8 zYIu>Ey@J_LENuu3yG^)RZH3wn<-Jfa#K=%Ug6Pu755${@fE@}u3LC5A9-UMi$l+Wf z^2QJf_2RW~EyK5Dw@8S!Jeqj0(II6$;K-q*X^M@ir17>(wq8j@)M(l@j-o0*E-@y! z)f#Xk#XE%g0U31d);kniz@nyAvct?l920p6D&!ae>05kW-tG2@7pB$@ur+=N-aWCZl;YH_pXejem!nnX)Z0ih?mK^P^ix;8u@f^*RhGB?D}* zs4DgHP|A78x`TZQ+TcT771{!uAw8_-JJQ%QqK!qP85KcWL1s#wPJdWkWZwLo@*(9z zrb=_FC+uL0Q9KQ*9aC)CNLWbq6lotIMPL#9&K0;e(^|BC7<%Aznpqdk7d_)U>zRUs zp0TyMOQL6vi|Uzmd$<|1i3Pml>=orG4qAMMnMlnP<`=9PG6k`Joz@dv=&`DsO?-&T z1j1Y98JJO&MHmwoS$eE{^FIZedHLZ0?^=mD5wNb7@TB)m-B*h}Z?;m}aehEcrh z^05Bf6?WKG}l^xX`m=jB$^WeFl>*EQYn+ zk0CqEXu(&C7A!hz!3!OKQXjZE zNwZT>UrG-p1$`9t;3b1RM0QPPHc2j*| zuXO-sPtv?cfW58>V7Cac_tgis!vWZ>^cr>x-X_3yHUaGY0_+3zfxX@Vm_12zhX8v+ z6Ts|Anh)0pR&fAkPttr;fW5H^V0Y8gtS&xYAK055fZ3BYpA=x%HUaEY0_@X4VDaZD z-s}L(o}~G#06Qr%Pd#^%1{*rb`@qo%3zw5L)$?Xxg;t3@Npp%!_}5dvI-R6>n}Y=0 zPyNnX<^hp_1yKoj?j#Lwpki5pg@+PzwJl4g~3>PMYB^=^kwwI^wQBc0k4 z?bPQ^(!j}Fy)Uc7LmKn!DKDD}R){;#8gkL8n;SeyQ&X~xmu`*7(>>1ebgzRv*^@L+ zi#(kbm8a)U(mZ#P2B(6aJ4y53>?DnoX6Y5p@)>8%@;L|1Vo%ciO*G58sAhTYB#qvD z<-XB2754?DMTlh2t#eAD{jT~m*<*nn~V`Y@v8$qZmV)p1A9CVG~BZ?09^o^8dIU(lA z9?n866$dw=83e+WCI(9RY*!a@|8f$nmX^Q~2o>0Ed4$Zt$dN$G>{-Z>WwfO_i5m#q zLG!M`Ky)jej+jr`un0FFLnyh4Ew9!Ty6XDZFdQ5x|CwKIiP`$yB5g1)1 zy`ckULAgg>2gpcgIkOoVEC%!CEERiXkxDqK78wUEW@J$$94E3<@}>qzbw0N0^Ree3 z9NS1d1<#F)~yrGiMHUsA9o7z^-XY+to(`(4CTBEi8y6Qw@n@^#k%| z%G}n6Y&Z1(VDy@LgqksD{89p^c9v`NWL!*~`OUL&uAy*Y$VN)_gOPhVntU$jB(ZuC zCkf#oq-^Gh$_ehVZ$=0QgjmIEIM1N4-kb;Rw!AU=}2}>NpfgBa0CkbHe8u{e?M;R z@hAFb&7!{^#9#j1bpOMMsH_}SC!wPn=2I$=NiR?l)*6> zBr)ab$Ib^_GKVusOzCjXl;C*dY>mUehYDVCvmFTF;NYyvQOXY?nUbs^{rXEblHma znh|RaBPz!`Vhq0|i1Y<*r>Pux$Opp6_EE_8;Xtt}WM$KJ{`;h2AzyL79Q_fnk&Fe9 zN!A1Yp|0W;OOVry-Ref;?^#I9)Iw#D1@j-fw`{GzPIu*~;`N2?c_;*P2?W*~9 z6$N)Eeg886+-X@aI84a!T>|2`hCr~v7RK>W!7*3in5%GntQHQRrIh*cae*?wAt*FB zLP&a4XB?wY9HUU&QxnC#^edzIRFf!BU3@5n!v74tiN&HrA?Z*^J{yDNTEX zDU9SkgT&8Z6!~-z5fb+dIyBYeUqlZq?Z@aEpSp4?W5iO+KDRngwL0IQMXy|jd-;SS z-Z2|r>@>k>5eczK_N4Y1$peBUYmuZvP*xZy=37z}{u$=mTpYmT6~I?2fM+%g@O*Mw zFu+FyV9o+GNSIYU21zWdPE<%%xRq_no(9JJJ6V>AKav|ny zMNB~vGfI!pfpDnX6Yx*QQRyCqdlgBZkhIN`6cU;_()O5YyQJE_Fwl04s?YhG&`7{2 zuvX8Qw9Z#7JUglN7^|mD_pQaQ=Z-UwK_rlxOb#@BYoYYaj@i*O~ik~qg& zb$zu#<6j)$ON_d{TD|@n#elatGQf6)Ro8EjCNHhsGoAYd85u)#RI0lQ%Woq_x~`k|wVTG^u2K_!R%Wcy+Sb z_GSg+Eeggh4a2xiD8F4`ToZs15GntIbO(>&Db?yfsa9`oxK(?A{4QzrbqTG0Oj>=r zYV}`KtM9Mfs?l3H|C_LZD_fRyZN%a6Qh6h#3m(ZAGUefoYjda=vN1HAJ7u-Jgnj>= zYbH_0VsvA+|&3<2Z04__XVg;PxJcqbo#gMvkm7c>q`op$!wD^MF$p983f`+!09Hx5d<447N&Brw3@PYPxnfY~{Ty$KAA zy$PXb$!P26l(a?oMf=q0@Q2 zd!{j#{h~7o7dns-aZWG^?3`$hged35N8xXEhP~YZcEppxu-TK*9PGWB^F>Zzs~Ijn z4gXSS6;r?ts{jS-e}x6c5-z9h*-W8ov4LEzrQ!&_rcx>@?qt!L z6|_@p>I(HDIp0{>lN@|=IV-f(NFbq9@8Hy&B&RC03&|(5s?Y;c3+p{I+S0(|EC)1h zWz5zKlZr$~nN) zs)ZmWnQaf%lu!X(WDxP`Edx)QHqaF1I-#ifQg^Alil{8I;bsWj(4DTiKuN(jpq&n~ zZ8mjy(7OsX^8i0UPqvw=2;!aeKjr1w;<2F3%Rn zCMjPciKXT?YtdA0JJ-7^ON>3j^cq&G;t}=ls-{;5e9gtex)+t#kn3%-lLEH}Iod(R zU1><1YnJ4!lZq#J1DWiWZJ83%g(D^h<&FmO1uCx8(sFJogF$Eqsp%`Vk#EwOfY zh>`3o8Pig~lv1l{VKwtGQgqVD2L>hZh+xT&Qt~?<1gdq^Tk1V)rHRg@)VLh`NbfhY ztOQ&QZ7rnhs)8puRudhr`uIXBE@j@OpNV@NiGdKZne&^nWFQ&^OzBkGX)Kd3LD4H< z>Iw6%kXj&>hLFp3nISe%C|CXnF%Oy0k$Y5Ed+LB{FkB&h%dCiMMNLOo{i^m=Bc^>~ z4Vge>q*UeI)pE5!N9h^2avGY(*8N5GjVvF~#85^FEUpc+zy^m=1XM7=Edm7ECCJR1 zLmh79Oi2&Z6MRD6#gEP55ve3xi(4up>Gg89act$eG}Or32-C)_WaB+BA9YYIydlAE z624;9!dnwcwIpS7|AfP^* zzVPmbE2xn7!C1($<>CKd_Pzv8uB@zgaE7gsMMea~fud(Hosh01Gek5q8R&F6Gnq*` zq0^bgHg;EcRXSDauIj0(PSS*75mc5&9ul6&st@F$0-}g4vdO*)g2>{6JbWtRuJ8~< zdH?_So$cN__x7zy0?5z&l&QY=o^!slef#om!f+Y7#_Hxn``!Z3dXY>FBeH~I7&$L! zowcO#fx^&!r~tGnn~FgDq=h~5{RiSls}QTlz7e>ExUNwt`F*K%g$Mp#L}L^AZvi8;$s<8y`Q`uPHLeO(<# zU&w>lpJc9Le+uV%%5t7a`D>7LX0Boz&aqz#j{Cg(#t{}vKsAPxreM(r@E(S}@6ATr z_aZq%SmQ5x7s;@zKMyHU(t*!|Wjqajldi>;wnVyAP}_@DERH+=Oo@;y2L>ilmjZkr zDruWQn2qXV1x_s#>UbZ8^^kwT z$*^X@9>Z>>uyO(Qz4GY`0Ot2nL9H(YOmzhu0QAD!8}~OC$B>3bu#32y7F6ij=2!H|EjYf(N!6S69*67GBJB#|K!bP+0HuHo{)m=@wu=)(ZMFa1h$Fugzd=m z)c$eQ=MWd|Cp*BNk^<~WxqvM=z~oo|`>C$!)3+bSIvqN6^U=ezW77a^&(Z0Lgzr$J zmV&FA3)iB9OMVGlhx%Et0Ioxm6SLEYW)F@{-ab1uabRqE|DnkvH|{^2gs+~0??f(q zCmnq9OW->^AbbMY#sDs-0E8o6&qei~Zh!wPcq{yZ{1N~+c02cItbBl8C+A|0asSM& z)2Qpg#2w<`{be`uSNKat-qX+0$jB&~ifU6Vmpv;Li93niGGg3UIe_?{r7@NV=W7m} zuRCz|^$!koJMGyNObL)0-(dG*+y0LNVExjw@U`sEHyv1gAyQo>RKr8PR1|w$n)TJb zlW^VTl@_k=;jid&dG9%|;k=ArbN3q0qS$5J^F!~dyYmZ?`X2<}{cJZyvpL4UsUc)$ zh;373#-H9da7|=&rr|*-pS`Fe@TB@-{s(Y_XUIxaaAY{ znuPDY4!)fV-%f?^eYx;`z`^%H2VY;eDY|~kwf15Pybn9@OzgssD0m;q1@EH{ypK8X z_V;tyqu{k`Q2fG}n_R$;I|v`G3;1Y-@DsTZe$qksM-IaCU%<`@0GI^t(+<2JRq%dP z!TU@ucz^7``x6IVe@s;DeyoBs?g8-69DpV!@#g0pfVZ6Y4i`Y9UvS{= z()HY>;QmDf+!#9jOABt}*%|2ci*5uHv2v>-*9YVx*H!B_>U_aH8vPkZWaO8i(O2_X z1dTqE&jpRn==@%qgGOJ#r~$#x&t@}0qkXYw2U;!Ul^W~9usFZktsR6(fmV&O=AxKo zYKFvdHm>Jg1+A+~{~V5;iAsNm5o7}6a~+I*v1nN^BGUm3duy3Mns9+t1@d{hKwfc+ z1@ie0$P2QN)mmo(mU8k2iG^(Z0>_bkKBbF!O5uD#E}SpqTUpE(IXI{KxprBL$-;Vd zm+b;yD1(3wc9mHo8;xh{IDBs7<#IwUSGdSi3wY^2*eT%~TySebb)WQ2U2XE5XKVO$s&RjjAz%x_be-=;8sFc;Fyj6kdz4Eh zzXWlA&pMt(5ce0{=zlGvA1vLa5a#|%JFoS=m~0~L!BO|W>jM3|Zoyv@giO@^D-IR@ zjRUo>Yq|lbim-P;^|M%qLMoH#`-Z~&4Tbq{b7B794(7kVh%jHhAKNaw=Ejup11igHY z&m!pMX?)JDIe3*B{|$Ng<#|qg{8=^>&hS7#hbd$T_cxEXU|aPZBD}~FgtYMt2i;x+ z9sVnt+Gp}jCT@9wyQk|8%)S`dd@!?Q-Nw&3cqa_J_^-hGoLqRH%eS&)Kkwk3yl~4( zr^w{Oo^yciGl1g10_gK|0ezuc*k5pf_9gZ9EbNl#6J|4)^cNkZ`wgV{uR!|3Tu8lS z@L~t)`7UXmFwl6Z1N0^XDE=#ezAP6|FB1H+1N8isH01UoVoT%Y4$_+qr1-Bu`ifjg zJ;i>dgY6xJNt+<1+H^^k!T{}otY8-dl+ zi`Vh3-2UIRur{9W>4ixa63YQ1==c}#X(BU-V835XKb5QdqV!X_CND`pl{5J=`!tz` z=ACYqQ)ZSchGP8^8dyz6Kbb||?){XJ{MwDp&Qv5AqpRn*#9Q zo6NesT`#9iwP%ty-0`hQ-r(0GBZb5L(rb2Z{rh7}c5eNF87p>f{fgWLJGcJO{PnU4 zBgzR3A4?dKoXv)5BgFkm(4G7ehe-KlJQFg~M%?isT#60SMx>mtr0X`@g}#<^hih$k z<}FLK2O>QxXsTnI<%LK6fw_w-)LQKam{ZCQ`&Q1c{H!>Q(u4Wl!UI zDFWXu>-rYYOVKGX>y&Gk%(|PNb!~4;{6gW5iLf&I)S9U?`FL~fTQ~K zavhVYpGr@?GgVtgf@_(y2IaVLF2CIeXIuiYLPP# zIHKh*#OVSHk@8cMg*cO5h@Utt8Hl@ir&mK9=WDzIW-=Otn0+@S%B#qXpVGipBttam z96sZxeyzZaQx0%4W3jme!eiNXcFaS`2Pg_1vcNPFj9Nh%r z$}a)xi30!?__0}_;!#siDgac<%}s!MvIlA;BjV_31-ef8u}RlI?YnOLU#L4ah?=4m zGjvt6^Noi={Y(L%Qr>I=)X(-0RN7fBAF!tv0L-h|#Gg$7d&U5OrM%TFVDT8OXBGe~ z<O z`mDLQiu~{6h<(5-PT)@0nW=MruSYv8wT1d|1p2zJbzU~GBfpQV zk)zt}U}&CJ&ewm3P+i8nbXn&HSE=5V@OC*UCZyQju9eE#Qmaxem!?l(E2N0MI09Ay zILqxIR)X8Xq9W~jwFW-oc!kkfHgAUpBm0p94_FW3%w9m~^9I-mmMmH>5iCE&qeCq> z(7Q!IaT4{M0thJO%PI&+d|9oa5~l{wE6{byn^n3#$94g3%Oz1JwVL>xDbG~_ z?1cjWCSIumnE0XQ1NNcR_K=t0OnaCaYhxu#2Ga}7q=Z9kQGu# z7Vttb1=EPifV+pyOE5s{GrxOyvSpunW=Q+W=7z?^-=6%5YaMbud&S+IuT9tC%Y*ba z&uj|5KwP~uKR7~1=FGQ8)zLZmUT9xFFB8p_<@|mdui)lJd6@@ZcF84sYsc$NP}>D{ zDJsp^z`@iJG%A6kf!4GX1f#kvD!LB{5RJ9gmx*?U>dFpCszl4A=oV*mq%R{K*;Yv6 zVLK8gAfq377wN{d33{!ioB+8q3d3wU(AvS8l1-P7fnjRGM{@vwktzx1n1Q6;?Go zpq%hpW#NRD-$RT`dzU~kT5_0XS1DA~x*Oniwa`059c$vAZEJ^DP-@SpJi*jDeQ%1BoRU##V+~?)ovaD zWG+Ynfb2d9C@&JK{aoUeLX7S!3y_yow1JQpi8jb*bbqZt*Qs~|>H62%E}&cm7+s@_ zk+=a%PUB4Os|x^?3NR3$UNbOI5+{%k)N2a>l?o^jpnju&pfpTCm=6gI$Or6o1prG$ z5C~wuIRIc16d-^}7(hN?zf}M*&#p2@4Rw zBrG5}^tWAs(zXL>uEb%6s;hV;&WIo&sA8znDFuEtyN8>#)3Zh|w7)v6+9vy97#p)& zm0l^;!bpY^j6HY`Q2lwbIY5)aaU*?Ld~P8-ctncdP9oljOj}}V;b$&VY1(MublEaL zF&&=W1ah@1U26krerKSe+pEDu_Iv8Sg11v9I_3a_V*p^#9~0M5EjM0i?x~GS+K(!} zm~OA47%RJx4IF^&bde;DH}a51+0M6J3Rgc9v!%u^sGS(mRdRmeUP~4no?RtfZ!I=V z{!~ne)@R@G8;N}YpJ5W;C3*^2q43m!Uh9%(jA8pqwvnT18PFhh$}5tfesU32Hj!g6 zS(BO1f8$%uNas$OmyCEKYakI&s9f7ENf4AQXkuL~%;~1V5_}7|!Eyu$uezP#(nz-@M^F`$%t2ZQqo%%EQaBj;LD-MYxuK@d+6HTz!Dk*& zhihI8gJXvGcaGGM!>|UVQ^Fs@3GQpP$Ev3)46fOSG*oy~>n`;2rB0B6jx&ev zC;*gq9DOr=vjwPk4h)opgXIJDdj){DQkBx)-ksP`8DDiz)(Kz*QppfpHJI8_PD$_MO&1pxCbw8U=- zU>_O)Fp0tvz$EU<0v2bXKU@GX&q7OpmH;LJS^;Xf?Z|*EG{kQ3&$;e#8*tIcK8t*I z-B~HW-bu02jvCsi(cQ!5`-~5LxTf;x(rd=I2t{?wi|e;u9Ts|$WW_Pf^X0>H*C^zn zd%5xC5{2JRxv1I+WW(#Ok$*Q|$3DaA`0vPH%JRg%tW_PiAM{zThTj@(DI_l+y-EK3 zd>C&R3E223k9L&tel+yGDaIQ-#b~!Rx=i;w!(D|crat)auvv^1pFPw@F2v5p{CLmN zg<)LTIc3UIj&snRp*lM=tZX(j@C>hYjx%9l37xDR#vlCrap;UT0!8LhHEW!(r+~S( z1BJH+wK<&F0>4kPvi>u>5hD;#88&EqC6SYQxzb)^UT-JU_(uLlCT3~W`pHP)hz41W zCN?FJk(lxuCyuX`ek?#-3x^}TG?Nsof{z21c4W1)DwD3P;$$w?n#_S&tM<$}V+fk- zow)Sv5^UBD2L0ycu8ZbX&{y$1_1ZMHP`+fT1Y-1IvqqBE3Km z_*OeUJR|B){B)`J-uPYOokA@C9~2L;Ipnhg10^xS`9S?~ z0iaUB#IjO<(mzld5iIHUMB><5~+~660$Di!-vHD*%{h zWF;6_0Fz+g;K1K@zw=^b^W!)Qax_Pz43OJBZ2P@9{AvEw zL%uJYK{XJDA1lAPBhX|8{ABNkSEoLV^titX6DOnOwd+uAZ5d_TN>*wB^{)#UZ~Pyg z%P1Fmd$0PJ?B_*bLoFkirTLAWtT+>>x!?7B+iSda!1(D0$-@}$9-GpF!(y!ebn~G9 zXx4*WCGM-!0=sWaco2MP#1B&3D}!~h6>p%CJq343&uzI5!qf_$q5E#0$L>T@%f7h9+6Mx~LASxqR9ucry#}X@60Gwxkk02yKx> z5BYrBUl-^)mFPjb{vz82WUBz5rWqWBo=V0BOHbpx+TRoaES1_p0Q=GafJv$b0ZdXe zqsTlGB`}+cbrE)e1VE-@xV3LVJ0F%rM`GEan0l>Vb zT~ag%VE;4#V3K)30Fz7$`GEa%0l>VrT~aa#VE-}zV3KG-0F%TD`GEav0l>VVA{iJ2 zurCh)m}FHDz$A-8K44!d0GJn6B;A4l_WuR|Oj0NaV3IPy0v2b#zghsWRBisb@G9x@|67OS=?zG@6~BCbm`w*b85e&6NRx zi<9`581YL8#g#M` zjk|pqzmgI|BYx@b!7n!m!%rpjkV_{CJ;F=(Il8MSE?q(r30DHk!s<@neYY+y#_W?~ zSNaZHF6rwMS3BM;d4i#quPmEOkd|Yh*7PDz&Isvt$q_cbBZaTVsq?=Y@imc4ps&)C z@z=m*b7?%F@HKc~foqV;_9E9nvb}`Y;I2Kihs9Za{UIB3hbv$!Der~5G)p3fMCugE z(1wD%&@q}eTy;i7?R+7%@%1WyZs4ytL?4E8VkeT`oU5kNAt4+GWEH+sp#Fun7;I%9D4k9A# zNfReLDRIU!TaG3ssw}fYa{^m9s_PcPETX3^i4N>wrXK5t=h_xv^T6#Y?bkAN>D7jd z5R0*)EViFA-7*vKa_W7Q4qm^)o`qzeNL7qhbH4pG?IPl!op^XZ_ z6&^!6zi5NNaV`ZpuhfNu-mqG7c@sAwU>CHivVY*}Qfrar?_};X-1J#q2UJ2vT|Cqc zoSPdCPRB417_>ro7;Yz?`kU?NZ#uHK8~EPUV2^RGS=?@EB*e4Cbn0qIuu5@d zhq!up+H6KRXWR(4S7g|b3C!e?3k)0flQ(XhBxiCrrI7>$ORRLftBnUw6a8$?XXDY+ zeMLu4=bBK?72ZY*OLa`bBEk(29}-f~Cp#$*sE@s^jx7vv;@$+}Lgdrlvs$QB_Fq!Q zPAM$oIeQ*jfFpVnYmJFvS$+ve^f0yyIHFvw;xn}LVTo<1CUA*_Orgqw3p(~$v~rjL zSnb4-Mx+e2E9&wF$T?D9ZGCehlUi@0e<6~yb;y^dWaYnc6iMGJzgT);eY?<5&9R^< zt1NYn)~_UAp^H1>Aj(hqw^6M*4`n>z-?o`e0wU#P33CuA?O$U{u!A+>Q*v`{O`f3` zm}G{&mnK-gm)X|L0%Lc3J=R2yko=O>c?_TNkfO7&z}OI9y1U` z^$xOcGQZ8J>4z4R5T!S4hz-2=fvH%-Vz(y3XN8|#w$pJRv z02^$IW4DEv)!V`>y-X7=q#E$hpfjj><3mhwxG_80gpg0v?b42KnlFBU@D zH?o-^?LLmLBUITq`z8lYOTlUBUfj&*1#tF32Uj15#KV=1vJW}HRur%m1?+GxU{ic6 zJ8{GTHeMV-dcc$;pG7oTgq$38u-&1s-J!7Ek_+3vyW#@3IoJl;Ov@4V)D!q|2UlC+ zYAakbxo{ohTUoQ8ba34?$eI~~A`9j{bdz^~&Vkrb5IYKDB^Sg62V&KMxKZ`TF1=iZ z-Bi5{*|D+(u^xmOBmq3`08G~OTg(N};Kb$4z8zJxEjCJOO+qb?f&gJRelB=3J3MGI`A`;D!(D1%8|-}PTuptyv)hp&vwB4 zP6jYAi3DLq5|D`Eobp@8u8qLLKr&I^?_i9&+bz+V%cbH{^T# zkfz4MWJAv3`|u*HC~@L&jrwXG^|d8Inz$K=2cjZhz`)?drU(-eTnv%*d@p-1u{H1{w%mr2Jgrv|8qQ#;GMg;i>9^sc+{ot&F6!gmWg=m zeKS>FS{J{{OR)7Ts2lt#60J}3f1V73z_D{9>-U_d07j|n-1?ALAD>&lUwR{KDVD7X z{=c^rOLE8!3$ch%K@PP15^Ie7G6sW;LM;DItHcH#U@=3_H`uOEL(hM(O~TOAi05*Q2g`^O#E4m$F^a`I>yrVj&R!SFs6L3sW4h%L)LN zG9CzEcO3w*2XI;fn3x3e0lQlPz*4pY0qpJr048<<0Zc3d`GDP{0AMK-f&g~U0RR(2 zfdD3^fqcM*3ILX}AqZgiIxoP)G>{M2_Z9#wWkV3a?mYltVkwXn6Wc&OVD~8im{&TD zx?6eQ0RR(2fdD3^fqcO3R{$`tbP^katl0Mr0GJpG1hAc)pam@MxqJTtfO$GCHUt4o zYzP4!TYqoTZ8zz!I5+3AD;zZ5?{L~-O%Cg|mg~sp@)mgSC8<;}l5$LRJ~*~l;|ph< zGUC2`p31_5yRdg^A%8n(B56F3>zwqZHvV>(T*{P>QfJDt&RTA%5;%Mc5ogJSdb3W? zf4^J-KZ`69+K)cd)D9!H@?yCJ=O`vgIgH%1HF!q3g-W!x%anAA@`bWp|DpSF32rH; zYi)Xhx?V&3L_D%U-MI|7{G)_H;XTM<0|~AM&V&uC7+2< z%FfDCWi8=Io@`*d=AkI1T-O5awmTcaGrIu6BfkKN{_?M#RPnfk7l^_xl#Ldp#ksx* z7eHxZaTG3BehEqw+hac0cSV7&J=Z7JN9p>ZY!_h5Tz4kJ^@)v9(70F??VRIW-@^(3 z=DEIuj_{QR08DI*0+?78^8tH!0l-q`M_IAq0RTJd*ikVl<^y(R0l>U!L~M@&*i{1n zCKg6nF|jG;1NMjlfTfI&0@x1^0GQYn1+YrUD#Xv&rUHPaOpXHB$N+$eJy8G?3u69? zZ7u+q=laCdD1dDl05Gv93SeSE%m?g|1pxD0pO_j2u&o0CCI&?TOiYORfQ=RaEM;pH zz{&#vw(4%B*b(yq+g1Rul)+H|+dcqbVp5b96C+|iU^@x`ma;YqU|078m^!kWwDwO4 zZ)BW#zor0CDN~~WC8oxJ9Ifx1s&!gRsFQ~3X|=`5!Wt>PrdeRb4+DM>qvP4|CNf&& z2d5;;xvk#=ok$im6NloAl#L(d6q0;#MvURE0+UUdGG(%2$_yr(OMCOdb{5UXV+w;k zRsigj-BZ98yXRnF-%D1zHQzu$m|1XV0d4Fn4Ec=(AWzvz1#+>I4g@*2MwGYjw94DY z&4r;oSOD6TiB+H#6YF_F>+G(L!-b)pDgbTDIxEnMbvA&OH&=qRPBAgPNDEuA`-q&x z1>Ry(%WUzvWTZ%9{>@FsCtqQ2?p9l{51A5?nsT?qu$a^3jJl263$MUTffY!Zeq{y3 z^c$|ggUt#U#3hOW01Y`QU#c*&1pOfGxpQXX#!nWW$6SGVq>RZj4>2Z(^VkvYB+5Uv z+N#B`EespHv??Lf3JPi(Uo&t?l z;jO%*z*gRurbUtT#gjm`QalNQtz3U+zVdQHl1fQoHqpNIqq|g>*o&gL9B2;;=`Ed) zMB`N9%{WtFGg3YjvKit-5pKq{22oV>UdU-R$d5Njh}?W-;pFK`8zp61%PWW*p06(< zBkKSd#Cc>i&i2mKXlS2?EX}gkeisEO-i%QxlaU3^&=DH|Ki|Tgg9^Lx}~_tG|Gf&!yAIhTE-y zsb8$ro2buAJVd2E2cu@*t?7`K`+WGlwms4)&NRB6X(OgA% zrziBCJzrbQM-`<_FJM*klAp!eFcLA;i7#SZB=xEnPZ6uD7${vGUJuLJgb=3nx<=FN zbh>tGrIFJoaAZKD+I5sL@;M1zgC&%zn;CaeZ^trvwm!aYgy2Ad^^pVsRXwacmvYYS zEX-KG3mFr8o~vw@vtS82G4D%{JJrQtin;Mw$b-OS_- z{@<0+dv4Mdf;$*t`{?D>j;;(@y-|PfH{fSQOa2fdu|it%I}4!el+&4@YjHYLbUo6D zD+-@dplh#M6|Xbt`lr|~pai*+y3vx0Q<*4n#EmRpiSyI~fO+-3_?rn}Pa6O*aVHbN z{*fF=K43px0I-y&nE>`P0{|voWCED@jpYOOvjqT4IhYAxPagmSCWvj3=|md3zBjs?f=ye z<6Vg9`@GY-{2UiIsgyIU&?_MGI6y=-=;lkVqsL_Jxd{865KyeHU#Kt*UgE2eL|HnV zU`E(YKshXhNPH9P1p5>?St0{4s1}RBDQQXMzM_Bzsf=7yiy4eSjnLYTl1!9E2k6)*`zrM%R7?wb zEHgx6qWP*1Ar(*JCMQXSZt@`61p3UB@LY~R36DrPI`(EtSY7GV+E93kuy^h6 zV+8S%mS2)3VarkZqC_b)%!|NHC4+d1$+#&PQ;sks&iST`_xs1`pPzDLlwW#qSD9R65R|)Nk!BpI8wvoGxNc!W3NmQK}HK(!!Qcfb>LK+)i9 zq-PM-F-@j7h;^(jO7*s;!h<>oO3ymmSgfJMb_kV9jX2mx>NmaTo${32_aMQX0x~xQ zrMiWwS)-)7+-kGzlT>UkA?ZFiK_pibs1gvt$EyOO!E(1Si&}Rw@-a|iThi}Z5c4JU zugIymBC`%>87PKmT?OCKBDYa*o2sK_5g?DKmz-F8uuK4#HJP9{Hf5>qSZ%S!5!A#b z2>TUg@k^+Nk4_yZgBcNLI#DK5Y=vqHb7{7kqmZHrSkQgFxv;c~@&h7_w@}$$MsQAO zsY#OO^_pF~61t*^B>=OPv?j}Z&A8+wNt3l5j00;+tx8oF;AUz7d*|(Z5Ub_V;rhZ! zGGfq!&)1gpYaB+}x2s%LD(K-$Xv(z8vYLTKy8+gq(nVP@tb-PEEm2_vjs0;H_X8AK z8raS$HRB%514ueF4@ZUZAbr=uD_r%&((OKLs{uAwm*?T=2;IZj`1t7d?K@a<3RSbn z1n!sx6WXmTQGv%Ve(XoL@4R|As4%Tip)(#Keld;$F0D?hbf7C$cDuEu+R6!}uG_(v zJ9q9J-M(Y{cH2Xt++X8F6;ZaaFZQZWhVdqj>d*{>Wg7n!`bWG6L~!#{&XHStKYmU& zR$8C~@^uc0hyt;*Qh^3n)U7(Z~RKWGn+If34Xq`);dHM;;cw*YtL^7XQlxBWb_)VlVg zE$9Ssvw$huz;9rsjUF0<*qiL90yGK9hJ{U#))hkEiCuH5^@F>w`FnMxii}|Rn-en6 zsyM%RrmIA4_(~V%uoZk!>BwfvRA8RjR&tMzpzd3(BQ~;?Rniu8hwT8fFG}xaq6Azj zbkVMgs=ZU1uK@+skS7^Sx>^%Vpd|t4xn$)MNlv6j>f8-oI_^6b^HjZpspz;^kIHfG6;oUG zU>XRhn31kUsnRK}REGq&>0mC`pmqgI#E7aitH*(#R;qsWMrNcLQB)5eAuh_tA<1U@ zD)a67$;xs@yQ@9!Ii}F0CTeoQj#%V~eyYdyOW;Ff{|4;Zq;>|i#Je?2s#kRL5r?O2 zr1Sd2_hg)Y$Y}JjpPfCYxBovDarl$Tl3oM`r!jUs7LaH?3HIQv&5hI>=-WsovU5V$ z`rT$_I*NnVAC?WI`Anp;JsTsn@`gQ`R0gksgsc4Kq{?AwSlwc--%W4qhOpYt1et!* zFQ-i}aN>@}gk8*riTQj#mw2Cjnn*2hTNP+$La(MpEII@*PP?@XX3|^&cLRA_DnvuA z)41Ev?=T#fd_#M`N%Sg6IoE;)jK%`Op?1eKN+@?+;#gr+!B<%vUIGI{yk+MK zBxR>bET^lKDz@y@)P?uzK;-onpx`OpwDuclL#4jAOzLeO+aDJsMS; zw<6~nD~&Ul<}M_&ifMwYag)6S_G*0TMtpX2`st6~f~N=4Pk(|>4{`zwJnYHRaod+K z?YT+{QlYEV4um{pa2I-A25n@1d8CR`Z7MEHX9ZR?2vQxK&-%g%n4~4zDZvvNUOy#6 zUFa*b0C{b(hBKdeWgJlM;t(V!Dv{5VeA^Zk z@kp+V${s;CF}$#_RI9XSd517j#6LB$9s4Rv9pWupZEo{cRHd8T#yr%4v?qkWbFvQh z4RVxeI^AAH%iw#6_0 zlX{z8Jus)FXEDhsZG{rUTtio%6L6A854iD*beUNbCqfy(x;s4)^i{I(Qn1sU$!{&qYQv^(tDYz7hnbJbjGi1Sgt7E6{tWF1g8-AjPCD3LL>6$B;N&E3Rl1oyaQ=vusADId6 zo!mgVM)K>L;S8FuOB&u6-?_UW(?u?bF%C5DM&aTpTC6(7gO|4ND-3v#ko__zWZ#Qd zBqC%tq3dAnN>MfhBDI&LX+|`OmJ7&J z%G848T}1({(63ax1Vam&h{8e;{#}KZCuT_`1`JX>v9^s%0(|P)Qp3%9OC% z;RMQFe3jQ_!~F7Sc~kg7iM8=96I@9t5a<=oc5Z8gSk&66AV;%#w^?BvGA z|9lPF2}XXP>{Ty$w0YW(0UP;s|Py0`ZcH|1cx-EKk8=B_`)&-NNfK%AaiUdK zUE@}L)Tu8b-To?cDCHrFzxoM7*Cm)_ZjOxM+}yQzKrIXTqc*%0sEu;iA@hzyvc$AQN-Me)t_?&X=S|LzY*JY*>v&4Ho#H(|zh%J5kO5Qs-W=78- zs+>n-KYMdaSyj%D7NG$SnyB&K*mJd%JJ%x=G_}qw6Mh>&L1vLY0m@?m+3t#DR(NX;n^>`Jn_QGY<}MA4WYn z7v%`7I9((}wAn$gU`#eRzH0Vy9#|q#%3ZK(8Ul&a2vrU#Sfy+0Gs&iz+g3K}MbODQ zBOfh;hBX47u?Eh$Ih`zSpzn}cUZW6lRBzPOMT4HHvpNr0E%zn;Sqvxc$BuQ%ylzke z)yg%&1ZD77^{SN7QoW1tf2cw>H;0}>nTp0f#iwfW!;~h@RFuumj_Fifm2SOpVroL4?=Ec`SL4$rFK}E}D)L$+6DAlpv>*-nX}cW`WLn2x!*3yNo9(LDtpFLAR!KXhBF97iNl?s3Mp-!Nve zKXP-ZCC2)9LtS7@_c~P#N-!VM!io<>(m*~D?!(q0^Z80;5y@a|cd+q`G+(8<4Z#Sdjb zH&1eEs6}Qf@-(%Wl6~>_<2=a4X-Cd7AXM$LAsG+ws~6}pf6uO+<%q?p?p^k2Qq4bu zE2?Bfd-fMKO-397me>{jS_)xW?b*oTLr10!X<%^zDCNgW;vxWD02|qu128%?2FR*) z#P&vV7195z&^#o%f7uza1zbdBKd?H2_>Mqt1#7YbsG&MgVA+`jkDY^twv&Z1U(WE4 zW0w1UcV7qAvtC}iopBsgA7zfi1Ea^`eRkvRaS;8_`_oUqbR+K62kg`2A^EC3Bv${N zeM&}4Tej%>ZP|h?9T83d_Dre?pmjT2E|>4z3>rClJ!`0z4v$Tb--wk41?+%6v-O@f z+)$9{s+;Iwre57aMcdWKdOMk$J1Sc7z;eW+sNEe#&}*%l+1HFfWcoZ7K$h95g#4LNVZ}4i3#;u;J4a$@6@GPEwB~Ar7^SyQy8osdT0Sdm~bM zLp{hPQU6prYt4mHqcsm@I{X@dL+3+22K+A?lYr3$YRVQZCT^M;zNt08zskTxy%iZi zi2(ypncHcm12+?c3_VZ&Z9n~~^)y;ru4=HR^g^*3TQSq}5;+~M@aL0? zB{-iR21Ola#KC2G6=Z_=+$kb6brDAOr)yN!u8H#JhOqPS%UMFKIW7&UIc|vr(IW)P zK}%0*IKLQk*08P%AK*Enr6h)47=??-XmiS7ZE#GDf`q4+9E!ae15BQX#9BBYn6yja zD(f)IQQ;)>u{XvHE*GZnagJsC?MT0hm(-^2^*>5?08J_dT^B`Iy(4(5S<{l+ z=d<}x`C|8_JK^K{+xK48lqmDE>==3kOLcXJ^hYq%YS{~?=QOkddf!Li(z|SP9}|Z) z&uu$!%Vs+rGs87I_;$1UC9uhPf{^eGkAh%LVu%P>uukCo(7*u8sbLZRcrZl0eh*7& zj14G5_2R!0bI`-02>Z`0YPL+Ya0{PHKF#D7{v6*0+=A`nIpichAAk2;zWYDPcS*9a zc2~=$6Z0-6Xo@#;JLZO?b`8LdsnH&G0SG@Mtw7J zV1WV{a-Bt8IE1mXT0ddwF>ke%fp0y_&+he9MmVK1rbk1+(~>@Bm33JZsLh zGc>~{Ct%4t8DGbzMvWpxoe?Y+@bn{aV>gE!=howU<(GHQX!H(LQ?w$jb|l*8nEy;S z2%nk2Gx45*6Ay%F|50R)yZcx=?Pvo>2~Sh7Bx)&!B%~;(Gqe3znVT%zWPR`(pgvi}~(v;_rl@eJTF#Wo-Ai z@pr$>cYlX>&QsWuNFjrNpKO&RgIn)z$zW&=GE$G!K5Ev^ zbPv>(Mw^}f&Jlxx+K}^R z5MW8Iwddiz)1zC6=!&^3)L*nI;4{@&8b|qmXHV;z6iY1x0B1^ z(TZ_6Lifob==Ity%nS^xNIh7)qqKe4vnmDI9&?xtd!%RGcY=etd*^olcVyu&BL72o zWJUkI43Kgzip#f_X1q}S52UsmdEY0TVg3;;9-!ib>6djbC?BEDoahUEmT5`D*b<828eq!!q&k&%8+WmttN z?s8UCx>7ffPMc!hLyMw}HSSHjkD z#vTiSkx=MEFnk<&th+Y(!q^h4Z5e5XT?CDL!*v3Quyod8Pd4k3lMK5$51Sv@LjdMJ zH-jlI0=POWb5;unM_cDQ)#0Q9q95T!AuA$?WBctmcNMToX&(~an;UhS@1^NM(P{YSD?)R{n;)&WfG-!p?8 zUQBhge$NA-v@^)Pj|Y+=D;=5bLA)G6Ur5E!>_WYOpYY7AwGF(bJ-p%$-q?2n{CMBM z$GQqQ=9m)w6t5Y)uC-uM2y<^hDcVef8n8y^DMRO4fE?-joo zfcoBh+q!>v-s9l8e`0TiG8%Ap_Pd*=asSY~-9hsM31}Q)HW2+-zYql-6otwudm3N7 z(e7CiDu?bBs>Y@Ow*`SHx4jDP1mh+=N@7tg^&J-nr6crkg}xJU_aL4k96)`bxL2in zHxTs-!WUQ>LcW5zYPnWrI0T$GAorOJS^iV7LKHVBieZGv=$a=aATV1*V3wp90IjvG z38JWn*KVM*9m|>uW(s=&=7DQcqar!LSUps`81)PwvvkuEVbV=UGRF^2WKc}oER)>s zY0a~#FJp6t;F3XYCc_@K| zYt*BFW#0=fR+(+G;ksYOGu3EUy*-7?QE#r+W?Ri!i2^YGM&c#p9gemJopvBB;b_UA z!Z`@nQaTQ|bGoAVF=2gR@>93_&|k?AWQ{7NN1ZtX*c2c&PDsQ!1!r>RNom}IIZC@a zgB5T*aTS;%P6h<`ETFitFalwL5dn!Jz(xn>)*kGg@m zfkq0Tay3k%1etB6&U{b@jeSPrIXITJ*uY3cgChc5R;f0X?Sn>02nhrb8Ou88AmmE2 zmuYPPXfGLF&?&16StwMMft$cFFh2tYEZveecGI*_*Cqc2w?zTHvD;lSKptue*FU<1 z9Pvx`SOGMosyl%IG*N}>LE2iXbr;IYh09UHoKEpb_yrYXzg|9wPcCY&QsjCkgy(s* zhzZYn&8N8{haBQHLi7ur5Iw;Q9}%L5aD}C<-G+#bPhqW?De*V|3LTi(+$#1u8g@{EdB0plrxp=2ef*6EYxV#q zn__s^>x=TPS-ten!20N$IDE<+8Rlf0U%DXp=Bsw$>~czh&wookjd3y0q*w0O@Lj}& z`4ztVdibatyQJapu|S?<5-BQ2XjEqr5Mp!XWbu1$+%&E+X<+RP1Y^y=ccQj% zl9vXKG2o}QgqBGhAR}~4h5*BSXr>#vG9dYS@8~~<6sbznr4cwUWr$@U@)t;NC+e_Dk!OD#G#Y($W1U;I*ny6xiCH~|9^87Gs=(hlZ&r1PIv2*aiXJ=#>gy%Vzirkfh0VAy-% zIzHC-MK>j2ks*zNudTY0in^NELEXJzoh?|&F+-q?xbqF%vki?24)J+$$A`hh5sOlG zgIIQW#A1*La`$}`BljZY^>>FC+r%n3&L|=L;%M_K%iP5&$$n5M}qH7GyqCnXdHVy1O#S*i?h7grYitw6|we>jnsnmlc$QWmlIzv$jQ$kE~yT#k_35#k`KJxF`ZO z+9bW0fwRs|znVG1xp?N>9neI`nt0~iBl#{#y6)m4v^el6#Me<-0U$yvM5GV7jSO(s zFKr-hMvNLkURjolgB1zn#M-XZu?{IHqizs5#m86M!2L+yjT9Y7Z%I0Ut(K%6@SR$Q zjQ|&iA}{*b-#Lsl9!$Cc6YO!55>BHA=H?*wiwzE^LM^g51z=5CM7T3#B+~`!nP{C3 zRqb?@ZD6kQg*j{1F9YXYsxBf&uCqPrJ3#2YA?(?+%|axZa^0XNokY{PS=!0aofZr2 zC!=P4{d<=Wb9BxR20z?$up_;GuZkD86YBZ&*-m^XHr+?}9Pvq@;_n3@f-w>--o5S9 zPV%2VjjO?S)+NzHMUh3Z|c`72m zm`#^k7Uh?;{(z;<#=+$~h%++B>plY>ulpp$Y;(e{+(Ug>`h=xFl|%Mp(L?rq_BbUD z+5PR)Abf0bT+=pRFOYmkD@)GuY;J`cwq8rrl9*Ed>3#8$>aPi7gmai zjdok3oEXlSpO~-lm6BZzHd2)qN{*?DiHaJd%Q1>xyEXR1jg;Ks;AQP~%yw!SX)9xe z-|i|+guMeGGQ6g&^#+Eek+0Z+x{ zN~B`wY9x9PqTkEjKgRF9#YNAf!tCL8>k~T6^%tvT7jDRCnqsDf3WHX{Hj!AU06*D_ z&W^P6*zQUnFH6J&fE zh^T}58w%A~BkLS!Ez;d1p&Uj z+G}G7MTjW;DMYsutSfAi_Py=T{_XYon%>3i8)8Bi5K!rIi0{?BG3GH6JDfmBX=}A@ zyGwr`TSLpL1rxQ8igR!FT%jbxDx*6X8vw@G{0RXDp#t0yQ^w8aBZ6ziHP<*x#!|Ai z1s1-APJMed?|NT;_Sc|A#8sg24>zJG>LB%(Ai{(EGvXNVX>w+2Kh|V%xR2_E4&f{r zE`GWVW3Sg6B6pi4<#mcgIEJjE9u)L=SPc~=`D^q^tC}G z+Ys(L)>Tq;u`(5OTh5K1)E`=L0u z3H^qh#GCj@Ul{YvXc57f>koGOGnGn3sw>?%^)U=3^SYJuiXhU>K&0AO<99-wYfJa6}iDrP&=t`bZ}QFuf>*8V~4IJDAb~Y0hY)!Mzc08%dxJ?-0f$Tgxj7 zjoGa0;i9P9^3kdNehlld<5B=HrV3cW)c7L5#WX28DJVto%8YARAVmZ$IBFg!O2Adz z)uw-pXUZ{~xd9m?qz0@)d6E%-#%be0ad4zPF z@17%Mw=nA@n7#XzB72u`<$onw#JJ7N88{QKX8r4q&#Z9+BYfu1Lw`33HLWkSl`cG3 z8lO74H=r`$)5el@nvJh1sh%y=Tsmy)Tsz=;OJxsL5ajoKR53tB1Im9E@feK}c;$YHg43BOem8Hiq)ZZ)*up&c{+>#2mk~)p)El37@GyH_fY;BS)E_DP zUR5!gC5-p2$ga2V#A+~_FI~aja|OQq(+|1rIplc2v?z1CN8A=kOWgYPum|+ISz1x% zF|-zJBntP6GC@JMheTo1V`}7>A^`=tz4R}|`07s&jP;C)KZf4T^e&sU-4k85+b%<< znUSzI&@)73!*gHqZUA8a4x!m4G%7wNW%o4D5!4b$~{? zn5g^OCkD=3Lqsd|Uq#9YXkZE2QrKPEUOF_jcVeov=XTqHZ=$h? zYzS@_=?9Mw-+^2`w(k;!ZHaVLKY-!&|U-=n>yf%CXEgRyqvPN2x2j6 z9EUnb>4)yldwD3{o)Ia$V6?@w&bvn!Ud9-Gz@Jo59x8HuqmQ$BoAro z-4$meCBL>$Ns&^Ji-ijt-E=iHlu?v4m32*4rcwbI4B!eNepuM%<`f5LItp`XO>T-7 zlI=35h@O6%m|||uom1Kc=ow+D68Py>b5RUy3E;q=B5c1~Uoolb_5qnLRob`+LLoX@ zfeDEUIHJ-93l;TplCalkX@pmo+O0{JSIMlCBg`33sw*~TfWhH}va$98;=9|BF8oaE zG9b{0(taJM@a&zL$^bL@%N^e%rw0rysSlXy`rH-?~lZ1U~Hq|VyuCz-;eMrRn{Q;|5S-6UEujtjuCW;u+6=Q{4iu|`yJ3x?un zV9IP#5f4am)jXrA7}z57pc~7xhxX_NxCgXno2cUfa$z(BlBy2dWIcRQwMBBT0nZo* zlaa~Ft W+}Y%OrpG6UM&?x|78K=L#R{sLv4 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 z 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 H*>V2?-MtVM literal 0 HcmV?d00001 diff --git a/docs_local/_build/.doctrees/index.doctree b/docs_local/_build/.doctrees/index.doctree new file mode 100644 index 0000000000000000000000000000000000000000..89d0e4aeb46bf0b3939e11055d61dc89a4b85bbb GIT binary patch literal 2971 zcma)8TWcIQ6n1Ry&FfuXl9ZCf#e_CZO4m+93nkDe=gqANN!q+P4B8p(N?0RJC5^p) z2s95VG3cQX_5b#hW_G;mry3TIj*iatJD>Dd>)(I+EAh__GnJN>3v(bqe`7p?^d9ZGA=SrrGMwyit8AfTCQZH&swa_I`8~rY_=@7f-Y>i zhcDFy)h`9H6Q%S07sQftstw&g5*%LlU%xo^;WQ4fzoD}b#{W9=(O1E?3xd$BXWOoq zG`X-f*RE4IaqEIh>QBlsaT~;1&Bvuh>LJxNo*AU&I;kMWK#1>_Bzg7K&B|iFC|$>r zX;^tSWcOH~-Dhj;fqQ-Q{PcH&j84gzik!m^JAFk9WloneA2pd*iH}}hTBhVFLNVYT_UCvKC=RFoN=a1oW*FT{0oGRtUT znLl1d`A88l;d)0(T9`A>y<49OHTGAo&3KjpK38r~8)^hyS>|J^Y37et+(rbCQBqt& zcLONAjw>$bUf5B|9SK9lPHVPj6_TYD|LQaewU0#lR9%KVw z_Qc(>nn;6iFm*|x)l^ec!MA0VC8DZbtrJUG24A<_FfDZ$j-(ld&AFr+3D-5$S}vy` zf}MsLCOY;o^RE2|5hYsQP(xppwkT~<5H$XT4NqKeN+s3AD($gA_S9IC7g+SQQqa_Z z&CIj5>sL`EaD9gDje1#Xfz<%*O2#BT3%iJ>6XE{ z#!*O1Y>9cTlv=Kp(Q!G2yWs`V5`~0@rX+`?hpweGq!i-gGL((VRAtceu2$aL|#K zL%6h@K4fB)wriNUqI4nwt-_ z83yKz>H|*gB>F%-4w=mbob@ajV}`SE-*nv`CL&K7YG@KjOc4-z<+jbGv}BerK4k(w zOwYPdVemFhPB7+wmf#eFn%$1;YFXXG5aSiFmokEQ0G?{vQSew!oIWdyc$ej21cV~h zM2A<#nFo4sp=F?fOWc)=QVZ3)DYVWKniDP(+}DA>qw}5mEd|($NkxoTZjH+G@LF-* ziju$q03~i?juEqoC3;FhafopT*Z{~ZW(zk1?&okTxZNdb^?rdE)+WuO9atGpAILjF zSbej%?2U1~Ah4GOB7E=J+bhHN@&66$8#h=ic|1KxIT{z-!(t@~(>d3x!d#S8dI%mZ z%EwAHU5u)CZU`Ig-jc=M+-p3vyJQ~}t*t-4!(Tx6c#yv zusTPz{4&FftG7a6@$O1#wgb>~U<@n$~ DlcAyz literal 0 HcmV?d00001 diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/claude_skill/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/claude_skill/index.rst.txt new file mode 100644 index 00000000..88631626 --- /dev/null +++ b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/claude_skill/index.rst.txt @@ -0,0 +1,15 @@ +PowerPlatform.Dataverse.claude_skill +==================================== + +.. py:module:: PowerPlatform.Dataverse.claude_skill + +.. autoapi-nested-parse:: + + Claude Code skill package for the PowerPlatform Dataverse Client SDK. + + This package contains two skills: + - dataverse-sdk-use: Guidance for using the SDK in your applications + - dataverse-sdk-dev: Guidance for developing/contributing to the SDK itself + + + diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/client/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/client/index.rst.txt new file mode 100644 index 00000000..3e3c485f --- /dev/null +++ b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/client/index.rst.txt @@ -0,0 +1,157 @@ +PowerPlatform.Dataverse.client +============================== + +.. py:module:: PowerPlatform.Dataverse.client + + +Classes +------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.client.DataverseClient + + +Module Contents +--------------- + +.. py:class:: DataverseClient(base_url: str, credential: azure.core.credentials.TokenCredential, config: Optional[PowerPlatform.Dataverse.core.config.DataverseConfig] = None, *, context: Optional[PowerPlatform.Dataverse.core.config.OperationContext] = None) + + High-level client for Microsoft Dataverse operations. + + This client provides a simple, stable interface for interacting with Dataverse environments + through the Web API. It handles authentication via Azure Identity and delegates HTTP operations + to an internal OData client. + + Key capabilities: + - OData CRUD operations: create, read, update, delete records + - SQL queries: execute read-only SQL via Web API ``?sql`` parameter + - Table metadata: create, inspect, and delete custom tables; create and delete columns + - File uploads: upload files to file columns with chunking support + + :param base_url: Your Dataverse environment URL, for example + ``"https://org.crm.dynamics.com"``. Trailing slash is automatically removed. + :type base_url: :class:`str` + :param credential: Azure Identity credential for authentication. + :type credential: ~azure.core.credentials.TokenCredential + :param config: Optional configuration for language, timeouts, and retries. + If not provided, defaults are loaded from :meth:`~PowerPlatform.Dataverse.core.config.DataverseConfig.from_env`. + :type config: ~PowerPlatform.Dataverse.core.config.DataverseConfig or None + :param context: Optional caller-defined context object appended to the + outbound ``User-Agent`` header for plugin/tool attribution. Cannot be used + together with ``config`` -- pass the context via + :class:`~PowerPlatform.Dataverse.core.config.DataverseConfig` instead. + :type context: ~PowerPlatform.Dataverse.core.config.OperationContext or None + + :raises ValueError: If ``base_url`` is missing or empty after trimming. + :raises ValueError: If both ``config`` and ``context`` are provided. + + .. note:: + The client lazily initializes its internal OData client on first use, allowing lightweight construction without immediate network calls. + + .. note:: + All methods that communicate with the Dataverse Web API may raise + :class:`~PowerPlatform.Dataverse.core.errors.HttpError` on non-successful + HTTP responses (e.g. 401, 403, 404, 429, 500). Individual method + docstrings document only domain-specific exceptions. + + Operations are organized into namespaces: + + - ``client.records`` -- create, update, delete, and get records (single or paginated queries) + - ``client.query`` -- query and search operations + - ``client.tables`` -- table and column metadata management + - ``client.files`` -- file upload operations + - ``client.dataframe`` -- pandas DataFrame wrappers for record CRUD + - ``client.batch`` -- batch multiple operations into a single HTTP request + + The client supports Python's context manager protocol for automatic resource + cleanup and HTTP connection pooling: + + .. rubric:: Example + + **Recommended -- context manager** (enables HTTP connection pooling):: + + from azure.identity import InteractiveBrowserCredential + from PowerPlatform.Dataverse.client import DataverseClient + + credential = InteractiveBrowserCredential() + + with DataverseClient("https://org.crm.dynamics.com", credential) as client: + record_id = client.records.create("account", {"name": "Contoso Ltd"}) + client.records.update("account", record_id, {"telephone1": "555-0100"}) + # Session closed, caches cleared automatically + + **Manual lifecycle**:: + + client = DataverseClient("https://org.crm.dynamics.com", credential) + try: + record_id = client.records.create("account", {"name": "Contoso Ltd"}) + finally: + client.close() + + + .. py:attribute:: auth + + + .. py:attribute:: records + + + .. py:attribute:: query + + + .. py:attribute:: tables + + + .. py:attribute:: files + + + .. py:attribute:: dataframe + + + .. py:attribute:: batch + + + .. py:method:: close() -> None + + Close the client and release resources. + + Closes the HTTP session (if any), clears internal caches, and + marks the client as closed. Safe to call multiple times. After + closing, any operation will raise :class:`RuntimeError`. + + Called automatically when using the client as a context manager. + + Example:: + + client = DataverseClient(base_url, credential) + try: + client.records.create("account", {"name": "Contoso"}) + finally: + client.close() + + + + .. py:method:: flush_cache(kind) -> int + + Flush cached client metadata or state. + + :param kind: Cache kind to flush. Currently supported values: + + - ``"picklist"``: Clears picklist label cache used for label-to-integer conversion + + Future kinds (e.g. ``"entityset"``, ``"primaryid"``) may be added without + breaking this signature. + :type kind: :class:`str` + + :return: Number of cache entries removed. + :rtype: :class:`int` + + .. rubric:: Example + + Clear the picklist cache:: + + removed = client.flush_cache("picklist") + print(f"Cleared {removed} cached picklist entries") + + + diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/common/constants/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/common/constants/index.rst.txt new file mode 100644 index 00000000..b843380a --- /dev/null +++ b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/common/constants/index.rst.txt @@ -0,0 +1,77 @@ +PowerPlatform.Dataverse.common.constants +======================================== + +.. py:module:: PowerPlatform.Dataverse.common.constants + +.. autoapi-nested-parse:: + + Constants for Dataverse Web API metadata types. + + These constants define the OData type identifiers used in Web API payloads + for metadata operations. + + + +Attributes +---------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.common.constants.ODATA_TYPE_LOCALIZED_LABEL + PowerPlatform.Dataverse.common.constants.ODATA_TYPE_LABEL + PowerPlatform.Dataverse.common.constants.ODATA_TYPE_LOOKUP_ATTRIBUTE + PowerPlatform.Dataverse.common.constants.ODATA_TYPE_ONE_TO_MANY_RELATIONSHIP + PowerPlatform.Dataverse.common.constants.ODATA_TYPE_MANY_TO_MANY_RELATIONSHIP + PowerPlatform.Dataverse.common.constants.CASCADE_BEHAVIOR_CASCADE + PowerPlatform.Dataverse.common.constants.CASCADE_BEHAVIOR_NO_CASCADE + PowerPlatform.Dataverse.common.constants.CASCADE_BEHAVIOR_REMOVE_LINK + PowerPlatform.Dataverse.common.constants.CASCADE_BEHAVIOR_RESTRICT + + +Module Contents +--------------- + +.. py:data:: ODATA_TYPE_LOCALIZED_LABEL + :value: 'Microsoft.Dynamics.CRM.LocalizedLabel' + + +.. py:data:: ODATA_TYPE_LABEL + :value: 'Microsoft.Dynamics.CRM.Label' + + +.. py:data:: ODATA_TYPE_LOOKUP_ATTRIBUTE + :value: 'Microsoft.Dynamics.CRM.LookupAttributeMetadata' + + +.. py:data:: ODATA_TYPE_ONE_TO_MANY_RELATIONSHIP + :value: 'Microsoft.Dynamics.CRM.OneToManyRelationshipMetadata' + + +.. py:data:: ODATA_TYPE_MANY_TO_MANY_RELATIONSHIP + :value: 'Microsoft.Dynamics.CRM.ManyToManyRelationshipMetadata' + + +.. py:data:: CASCADE_BEHAVIOR_CASCADE + :value: 'Cascade' + + + Perform the action on all referencing table records associated with the referenced table record. + +.. py:data:: CASCADE_BEHAVIOR_NO_CASCADE + :value: 'NoCascade' + + + Do not apply the action to any referencing table records associated with the referenced table record. + +.. py:data:: CASCADE_BEHAVIOR_REMOVE_LINK + :value: 'RemoveLink' + + + Remove the value of the referencing column for all referencing table records when the referenced record is deleted. + +.. py:data:: CASCADE_BEHAVIOR_RESTRICT + :value: 'Restrict' + + + Prevent the referenced table record from being deleted when referencing table records exist. + diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/common/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/common/index.rst.txt new file mode 100644 index 00000000..55ae1b6f --- /dev/null +++ b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/common/index.rst.txt @@ -0,0 +1,22 @@ +PowerPlatform.Dataverse.common +============================== + +.. py:module:: PowerPlatform.Dataverse.common + +.. autoapi-nested-parse:: + + Common utilities and constants for the Dataverse SDK. + + This module contains shared constants and utilities used across the SDK. + + + +Submodules +---------- + +.. toctree:: + :maxdepth: 1 + + /autoapi/PowerPlatform/Dataverse/common/constants/index + + diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/core/config/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/core/config/index.rst.txt new file mode 100644 index 00000000..ef0f6d1d --- /dev/null +++ b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/core/config/index.rst.txt @@ -0,0 +1,117 @@ +PowerPlatform.Dataverse.core.config +=================================== + +.. py:module:: PowerPlatform.Dataverse.core.config + +.. autoapi-nested-parse:: + + Dataverse client configuration. + + Provides :class:`~PowerPlatform.Dataverse.core.config.DataverseConfig`, a lightweight + immutable container for locale and (reserved) HTTP tuning options plus the + convenience constructor :meth:`~PowerPlatform.Dataverse.core.config.DataverseConfig.from_env`. + + + +Classes +------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.core.config.OperationContext + PowerPlatform.Dataverse.core.config.DataverseConfig + + +Module Contents +--------------- + +.. py:class:: OperationContext + + Caller-defined context appended to outbound ``User-Agent`` headers. + + The context string is validated to be semicolon-separated ``key=value`` pairs + using only allowed keys (``app``, ``skill``, ``agent``) with values from + closed allowlists. Free-form text, email addresses, PII, and unknown keys + are rejected. + + :param user_agent_context: Attribution string in ``key=value;key=value`` format. + :type user_agent_context: :class:`str` + + :raises ValueError: If the string is empty, contains control characters, + does not match the required ``key=value`` format, or uses unknown + keys/values. + + + .. py:attribute:: user_agent_context + :type: str + + +.. py:class:: DataverseConfig + + Configuration settings for Dataverse client operations. + + :param language_code: LCID (Locale ID) for localized labels and messages. Default is 1033 (English - United States). + :type language_code: :class:`int` + :param http_retries: Optional maximum number of retry attempts for transient HTTP errors. Reserved for future use. + :type http_retries: :class:`int` or None + :param http_backoff: Optional backoff multiplier (in seconds) between retry attempts. Reserved for future use. + :type http_backoff: :class:`float` or None + :param http_timeout: Optional request timeout in seconds. Reserved for future use. + :type http_timeout: :class:`float` or None + :param log_config: Optional local HTTP diagnostics logging configuration. + When provided, all HTTP requests and responses are logged to timestamped + ``.log`` files with automatic redaction of sensitive headers. + :type log_config: ~PowerPlatform.Dataverse.core.log_config.LogConfig or None + :param operation_context: Optional caller-defined context object appended to the + outbound ``User-Agent`` header as a parenthesized comment. Intended for + plugin/tool attribution. + :type operation_context: ~PowerPlatform.Dataverse.core.config.OperationContext or None + + + .. py:attribute:: language_code + :type: int + :value: 1033 + + + + .. py:attribute:: http_retries + :type: Optional[int] + :value: None + + + + .. py:attribute:: http_backoff + :type: Optional[float] + :value: None + + + + .. py:attribute:: http_timeout + :type: Optional[float] + :value: None + + + + .. py:attribute:: log_config + :type: Optional[PowerPlatform.Dataverse.core.log_config.LogConfig] + :value: None + + + + .. py:attribute:: operation_context + :type: Optional[OperationContext] + :value: None + + + + .. py:method:: from_env() -> DataverseConfig + :classmethod: + + + Create a configuration instance with default settings. + + :return: Configuration instance with default values. + :rtype: ~PowerPlatform.Dataverse.core.config.DataverseConfig + + + diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/core/errors/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/core/errors/index.rst.txt new file mode 100644 index 00000000..7fb487a0 --- /dev/null +++ b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/core/errors/index.rst.txt @@ -0,0 +1,185 @@ +PowerPlatform.Dataverse.core.errors +=================================== + +.. py:module:: PowerPlatform.Dataverse.core.errors + +.. autoapi-nested-parse:: + + Structured Dataverse exception hierarchy. + + This module provides :class:`~PowerPlatform.Dataverse.core.errors.DataverseError` and + specialized :class:`~PowerPlatform.Dataverse.core.errors.ValidationError`, + :class:`~PowerPlatform.Dataverse.core.errors.MetadataError`, + :class:`~PowerPlatform.Dataverse.core.errors.SQLParseError`, and + :class:`~PowerPlatform.Dataverse.core.errors.HttpError` for validation, metadata, + SQL parsing, and Web API HTTP failures. + + + +Exceptions +---------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.core.errors.DataverseError + PowerPlatform.Dataverse.core.errors.ValidationError + PowerPlatform.Dataverse.core.errors.MetadataError + PowerPlatform.Dataverse.core.errors.SQLParseError + PowerPlatform.Dataverse.core.errors.HttpError + + +Module Contents +--------------- + +.. py:exception:: DataverseError(message: str, code: str, subcode: Optional[str] = None, status_code: Optional[int] = None, details: Optional[Dict[str, Any]] = None, source: Optional[str] = None, is_transient: bool = False) + + Bases: :py:obj:`Exception` + + + Base structured exception for the Dataverse SDK. + + :param message: Human-readable error message. + :type message: :class:`str` + :param code: Error category code (e.g. ``"validation_error"``, ``"http_error"``). + :type code: :class:`str` + :param subcode: Optional subcategory or specific error identifier. + :type subcode: :class:`str` | None + :param status_code: Optional HTTP status code if the error originated from an HTTP response. + :type status_code: :class:`int` | None + :param details: Optional dictionary containing additional diagnostic information. + :type details: :class:`dict` | None + :param source: Error source, either ``"client"`` or ``"server"``. + :type source: :class:`str` + :param is_transient: Whether the error is potentially transient and may succeed on retry. + :type is_transient: :class:`bool` + + Initialize self. See help(type(self)) for accurate signature. + + + .. py:attribute:: message + + + .. py:attribute:: code + + + .. py:attribute:: subcode + :value: None + + + + .. py:attribute:: status_code + :value: None + + + + .. py:attribute:: details + + + .. py:attribute:: source + :value: 'client' + + + + .. py:attribute:: is_transient + :value: False + + + + .. py:attribute:: timestamp + + + .. py:method:: to_dict() -> Dict[str, Any] + + Convert the error to a dictionary representation. + + :return: Dictionary containing all error properties. + :rtype: :class:`dict` + + + +.. py:exception:: ValidationError(message: str, *, subcode: Optional[str] = None, details: Optional[Dict[str, Any]] = None) + + Bases: :py:obj:`DataverseError` + + + Exception raised for client-side validation failures. + + :param message: Human-readable validation error message. + :type message: :class:`str` + :param subcode: Optional specific validation error identifier. + :type subcode: :class:`str` | None + :param details: Optional dictionary with additional validation context. + :type details: :class:`dict` | None + + Initialize self. See help(type(self)) for accurate signature. + + +.. py:exception:: MetadataError(message: str, *, subcode: Optional[str] = None, details: Optional[Dict[str, Any]] = None) + + Bases: :py:obj:`DataverseError` + + + Exception raised for metadata operation failures. + + :param message: Human-readable metadata error message. + :type message: :class:`str` + :param subcode: Optional specific metadata error identifier. + :type subcode: :class:`str` | None + :param details: Optional dictionary with additional metadata context. + :type details: :class:`dict` | None + + Initialize self. See help(type(self)) for accurate signature. + + +.. py:exception:: SQLParseError(message: str, *, subcode: Optional[str] = None, details: Optional[Dict[str, Any]] = None) + + Bases: :py:obj:`DataverseError` + + + Exception raised for SQL query parsing failures. + + :param message: Human-readable SQL parsing error message. + :type message: :class:`str` + :param subcode: Optional specific SQL parsing error identifier. + :type subcode: :class:`str` | None + :param details: Optional dictionary with SQL query context and parse information. + :type details: :class:`dict` | None + + Initialize self. See help(type(self)) for accurate signature. + + +.. py:exception:: HttpError(message: str, status_code: int, is_transient: bool = False, subcode: Optional[str] = None, service_error_code: Optional[str] = None, correlation_id: Optional[str] = None, client_request_id: Optional[str] = None, service_request_id: Optional[str] = None, traceparent: Optional[str] = None, body_excerpt: Optional[str] = None, retry_after: Optional[int] = None, details: Optional[Dict[str, Any]] = None) + + Bases: :py:obj:`DataverseError` + + + Exception raised for HTTP request failures from the Dataverse Web API. + + :param message: Human-readable HTTP error message, typically from the API error response. + :type message: :class:`str` + :param status_code: HTTP status code (e.g. 400, 404, 500). + :type status_code: :class:`int` + :param is_transient: Whether the error is transient (429, 503, 504) and may succeed on retry. + :type is_transient: :class:`bool` + :param subcode: Optional HTTP status category (e.g. ``"4xx"``, ``"5xx"``). + :type subcode: :class:`str` | None + :param service_error_code: Optional Dataverse-specific error code from the API response. + :type service_error_code: :class:`str` | None + :param correlation_id: Optional client-generated correlation ID for tracking requests within an SDK call. + :type correlation_id: :class:`str` | None + :param client_request_id: Optional client-generated request ID injected into outbound headers. + :type client_request_id: :class:`str` | None + :param service_request_id: Optional ``x-ms-service-request-id`` value returned by Dataverse servers. + :type service_request_id: :class:`str` | None + :param traceparent: Optional W3C trace context for distributed tracing. + :type traceparent: :class:`str` | None + :param body_excerpt: Optional excerpt of the response body for diagnostics. + :type body_excerpt: :class:`str` | None + :param retry_after: Optional number of seconds to wait before retrying (from Retry-After header). + :type retry_after: :class:`int` | None + :param details: Optional additional diagnostic details. + :type details: :class:`dict` | None + + Initialize self. See help(type(self)) for accurate signature. + + diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/core/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/core/index.rst.txt new file mode 100644 index 00000000..31a27202 --- /dev/null +++ b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/core/index.rst.txt @@ -0,0 +1,25 @@ +PowerPlatform.Dataverse.core +============================ + +.. py:module:: PowerPlatform.Dataverse.core + +.. autoapi-nested-parse:: + + Core infrastructure components for the Dataverse SDK. + + This module contains the foundational components including authentication, + configuration, HTTP client, and error handling. + + + +Submodules +---------- + +.. toctree:: + :maxdepth: 1 + + /autoapi/PowerPlatform/Dataverse/core/config/index + /autoapi/PowerPlatform/Dataverse/core/errors/index + /autoapi/PowerPlatform/Dataverse/core/log_config/index + + diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/core/log_config/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/core/log_config/index.rst.txt new file mode 100644 index 00000000..aa4a5e09 --- /dev/null +++ b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/core/log_config/index.rst.txt @@ -0,0 +1,90 @@ +PowerPlatform.Dataverse.core.log_config +======================================= + +.. py:module:: PowerPlatform.Dataverse.core.log_config + +.. autoapi-nested-parse:: + + Local file logging configuration for Dataverse SDK HTTP diagnostics. + + Provides :class:`~PowerPlatform.Dataverse.core.log_config.LogConfig`, an opt-in configuration for writing request/response + traces to ``.log`` files with automatic header redaction and timestamped filenames. + + + +Classes +------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.core.log_config.LogConfig + + +Module Contents +--------------- + +.. py:class:: LogConfig + + Configuration for local HTTP diagnostics logging. + + When provided to :class:`~PowerPlatform.Dataverse.client.DataverseClient` via + :class:`~PowerPlatform.Dataverse.core.config.DataverseConfig`, every HTTP request + and response is logged to timestamped ``.log`` files in the specified folder. + Sensitive headers (e.g. ``Authorization``) are automatically redacted. + + :param log_folder: Directory path for log files. Created automatically if missing. + Default: ``"./dataverse_logs"`` + :param log_file_prefix: Filename prefix. Timestamp is appended automatically. + Default: ``"dataverse"`` → ``dataverse_20260310_143022.log`` + :param max_body_bytes: Maximum bytes of request/response body to capture. + ``0`` (default) disables body capture. Enable only for active debugging + sessions — bodies may contain PII and sensitive business data. + :param redacted_headers: Header names (case-insensitive) whose values are + replaced with ``"[REDACTED]"`` in logs. Defaults include + ``Authorization``, ``Proxy-Authorization``, etc. + :param log_level: Python logging level name. Default: ``"DEBUG"``. + :param max_file_bytes: Max size per log file before rotation (bytes). + Default: ``10_485_760`` (10 MB). + :param backup_count: Number of rotated backup files to keep. Default: ``5``. + + + .. py:attribute:: log_folder + :type: str + :value: './dataverse_logs' + + + + .. py:attribute:: log_file_prefix + :type: str + :value: 'dataverse' + + + + .. py:attribute:: max_body_bytes + :type: int + :value: 0 + + + + .. py:attribute:: redacted_headers + :type: FrozenSet[str] + + + .. py:attribute:: log_level + :type: str + :value: 'DEBUG' + + + + .. py:attribute:: max_file_bytes + :type: int + :value: 10485760 + + + + .. py:attribute:: backup_count + :type: int + :value: 5 + + + diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/data/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/data/index.rst.txt new file mode 100644 index 00000000..62e70f16 --- /dev/null +++ b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/data/index.rst.txt @@ -0,0 +1,14 @@ +PowerPlatform.Dataverse.data +============================ + +.. py:module:: PowerPlatform.Dataverse.data + +.. autoapi-nested-parse:: + + Data access layer for the Dataverse SDK. + + This module contains OData protocol handling, CRUD operations, metadata management, + SQL query functionality, and file upload capabilities. + + + diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/extensions/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/extensions/index.rst.txt new file mode 100644 index 00000000..4f88c87c --- /dev/null +++ b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/extensions/index.rst.txt @@ -0,0 +1,11 @@ +PowerPlatform.Dataverse.extensions +================================== + +.. py:module:: PowerPlatform.Dataverse.extensions + +.. autoapi-nested-parse:: + + Optional extensions for the Dataverse SDK. Currently a placeholder. + + + diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/index.rst.txt new file mode 100644 index 00000000..8f38b9f8 --- /dev/null +++ b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/index.rst.txt @@ -0,0 +1,24 @@ +PowerPlatform.Dataverse +======================= + +.. py:module:: PowerPlatform.Dataverse + + +Submodules +---------- + +.. toctree:: + :maxdepth: 1 + + /autoapi/PowerPlatform/Dataverse/claude_skill/index + /autoapi/PowerPlatform/Dataverse/client/index + /autoapi/PowerPlatform/Dataverse/common/index + /autoapi/PowerPlatform/Dataverse/core/index + /autoapi/PowerPlatform/Dataverse/data/index + /autoapi/PowerPlatform/Dataverse/extensions/index + /autoapi/PowerPlatform/Dataverse/migration/index + /autoapi/PowerPlatform/Dataverse/models/index + /autoapi/PowerPlatform/Dataverse/operations/index + /autoapi/PowerPlatform/Dataverse/utils/index + + diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/migration/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/migration/index.rst.txt new file mode 100644 index 00000000..15bc1e31 --- /dev/null +++ b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/migration/index.rst.txt @@ -0,0 +1,15 @@ +PowerPlatform.Dataverse.migration +================================= + +.. py:module:: PowerPlatform.Dataverse.migration + + +Submodules +---------- + +.. toctree:: + :maxdepth: 1 + + /autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index + + diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index.rst.txt new file mode 100644 index 00000000..0e6e73c2 --- /dev/null +++ b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index.rst.txt @@ -0,0 +1,119 @@ +PowerPlatform.Dataverse.migration.migrate_v0_to_v1 +================================================== + +.. py:module:: PowerPlatform.Dataverse.migration.migrate_v0_to_v1 + +.. autoapi-nested-parse:: + + DV-Python-SDK v0 -> v1 GA migration codemod. + + Mechanically rewrites beta (0.1.0b*) call sites to their GA (1.0) equivalents + using LibCST (concrete syntax tree — preserves all whitespace and comments). + + Usage:: + + pip install PowerPlatform-Dataverse-Client[migration] + dataverse-migrate path/to/your/scripts/ + dataverse-migrate path/to/your/scripts/ --dry-run # preview without writing + dataverse-migrate path/to/your/scripts/ --client-var=svc # if client is named 'svc' + + # Or via module for development installs: + python -m PowerPlatform.Dataverse.migration.migrate_v0_to_v1 path/to/your/scripts/ + + Transformations applied + ----------------------- + Builder methods (.filter_* -> .where(col(...)...)):: + + .filter_eq("col", v) -> .where(col("col") == v) + .filter_ne("col", v) -> .where(col("col") != v) + .filter_gt("col", v) -> .where(col("col") > v) + .filter_ge("col", v) -> .where(col("col") >= v) + .filter_lt("col", v) -> .where(col("col") < v) + .filter_le("col", v) -> .where(col("col") <= v) + .filter_contains("col", v) -> .where(col("col").contains(v)) + .filter_startswith("col", v) -> .where(col("col").startswith(v)) + .filter_endswith("col", v) -> .where(col("col").endswith(v)) + .filter_in("col", vals) -> .where(col("col").in_(vals)) + .filter_not_in("col", vals) -> .where(col("col").not_in(vals)) + .filter_null("col") -> .where(col("col").is_null()) + .filter_not_null("col") -> .where(col("col").is_not_null()) + .filter_between("col", lo, hi) -> .where(col("col").between(lo, hi)) + .filter_not_between("col", lo, hi) -> .where(col("col").not_between(lo, hi)) + .filter_raw("expr") -> .where(raw("expr")) + .filter("expr") -> .where(raw("expr")) + .execute(by_page=True) -> .execute_pages() + .execute(by_page=False) -> .execute() (flag removed) + .to_dataframe() -> .execute().to_dataframe() + Inserts .execute() when the receiver is a recognised QueryBuilder chain + (contains .builder(), .select(), .where(), or a .filter_*() call). + + Record namespace:: + + batch.records.get(t, id) -> batch.records.retrieve(t, id) + + Top-level shortcuts (removed at GA):: + + client.create(t, d) -> client.records.create(t, d) + client.update(t, id, d) -> client.records.update(t, id, d) + client.delete(t, id) -> client.records.delete(t, id) + client.get(t, id) -> client.records.get(t, id) [deprecated; see manual section] + client.query_sql(sql) -> client.query.sql(sql) + client.get_table_info(t) -> client.tables.get(t) + client.create_table(t, …) -> client.tables.create(t, …) + client.delete_table(t) -> client.tables.delete(t) + client.list_tables() -> client.tables.list() + client.create_columns(t, …) -> client.tables.add_columns(t, …) + client.delete_columns(t, …) -> client.tables.remove_columns(t, …) + client.upload_file(…) -> client.files.upload(…) + + Import management: + Adds ``from PowerPlatform.Dataverse.models.filters import col`` when a + .filter_* method is rewritten (if col is not already imported). + Adds ``raw`` to the same import when .filter_raw or .filter is rewritten. + + NOT handled by this codemod (manual migration required): + execute(by_page=variable) -> manual review required (variable argument, not literal) + client.records.get(t, id) -> client.records.retrieve(t, id) + Return type changes: beta returns Record (raises on 404); GA retrieve() returns + Record | None. Callers that do not guard against None will fail silently. + client.records.get(t, kw=…) -> client.records.list(t, kw=…) + Return type changes: beta returns Iterable[List[Record]] (pages); GA list() + returns QueryResult (flat iterable over Records). Any ``for page in result: + for rec in page:`` iteration pattern breaks after a mechanical rename. + client.dataframe.get() -> client.query.builder(…).execute().to_dataframe() + Expression reconstruction requires understanding caller intent. + client.query.sql_select()/sql_join()/sql_joins() -> removed (no mechanical replacement) + + + +Functions +--------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.migration.migrate_v0_to_v1.find_manual_patterns + PowerPlatform.Dataverse.migration.migrate_v0_to_v1.migrate_source + PowerPlatform.Dataverse.migration.migrate_v0_to_v1.migrate_file + PowerPlatform.Dataverse.migration.migrate_v0_to_v1.main + + +Module Contents +--------------- + +.. py:function:: find_manual_patterns(source: str, *, client_var: str = 'client') -> List[str] + + Return descriptions of patterns in *source* that require manual migration. + + +.. py:function:: migrate_source(source: str, *, client_var: str = 'client') -> str + + Parse *source*, apply transformations, return migrated source. + + +.. py:function:: migrate_file(path: pathlib.Path, *, dry_run: bool = False, client_var: str = 'client') -> Tuple[bool, List[str]] + + Migrate *path* in place. Returns (was_changed, manual_review_notes). + + +.. py:function:: main(argv: Optional[List[str]] = None) -> int + diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/batch/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/batch/index.rst.txt new file mode 100644 index 00000000..a0d3d118 --- /dev/null +++ b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/batch/index.rst.txt @@ -0,0 +1,154 @@ +PowerPlatform.Dataverse.models.batch +==================================== + +.. py:module:: PowerPlatform.Dataverse.models.batch + +.. autoapi-nested-parse:: + + Public result types for batch operations. + + + +Classes +------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.models.batch.BatchItemResponse + PowerPlatform.Dataverse.models.batch.BatchResult + + +Module Contents +--------------- + +.. py:class:: BatchItemResponse + + Response from a single operation within a batch request. + + Responses are returned in submission order. For operations added to a + changeset, responses appear in the changeset's position in that order. + + :param status_code: HTTP status code for this operation (e.g. 204, 200, 400). + :param content_id: ``Content-ID`` value from the changeset response part, if any. + :param entity_id: GUID extracted from the ``OData-EntityId`` response header. + Set for successful create (POST) operations. + :param data: Parsed JSON response body (e.g. for GET operations). + :param error_message: Error message when the operation failed. + :param error_code: Service error code when the operation failed. + + Example:: + + for item in result.responses: + if item.is_success: + print(f"[OK] {item.status_code} entity_id={item.entity_id}") + else: + print(f"[ERR] {item.status_code}: {item.error_message}") + + + .. py:attribute:: status_code + :type: int + + + .. py:attribute:: content_id + :type: Optional[str] + :value: None + + + + .. py:attribute:: entity_id + :type: Optional[str] + :value: None + + + + .. py:attribute:: data + :type: Optional[Dict[str, Any]] + :value: None + + + + .. py:attribute:: error_message + :type: Optional[str] + :value: None + + + + .. py:attribute:: error_code + :type: Optional[str] + :value: None + + + + .. py:property:: is_success + :type: bool + + + Return True when status_code is 2xx. + + +.. py:class:: BatchResult + + Result of executing a batch request. + + Contains one :class:`BatchItemResponse` per HTTP operation submitted. + Operations that expand to multiple HTTP requests (e.g. ``add_columns`` + with three columns) contribute three entries. + + :param responses: All responses in submission order. + + Example:: + + result = client.batch.new().execute() + print(f"Succeeded: {len(result.succeeded)}, Failed: {len(result.failed)}") + for guid in result.entity_ids: + print(f"[OK] entity_id: {guid}") + + + .. py:attribute:: responses + :type: List[BatchItemResponse] + :value: [] + + + + .. py:property:: succeeded + :type: List[BatchItemResponse] + + + Responses with 2xx status codes. + + + .. py:property:: failed + :type: List[BatchItemResponse] + + + Responses with non-2xx status codes. + + + .. py:property:: has_errors + :type: bool + + + True when any response has a non-2xx status code. + + + .. py:property:: entity_ids + :type: List[str] + + + GUIDs extracted from ``OData-EntityId`` headers of successful responses. + + Returns entity IDs from any successful (2xx) response that includes an + ``OData-EntityId`` header. Both individual ``POST`` (create) and + ``PATCH`` (update) operations return this header with the record's GUID. + ``GET`` and ``DELETE`` operations do not. + + .. note:: + ``CreateMultiple`` and ``UpsertMultiple`` action responses do **not** + return per-record ``OData-EntityId`` headers. Their IDs are in the + JSON response body (``data["Ids"]``). Access them via:: + + for resp in result.succeeded: + if resp.data and "Ids" in resp.data: + bulk_ids = resp.data["Ids"] + + diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index.rst.txt new file mode 100644 index 00000000..1632cc4a --- /dev/null +++ b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index.rst.txt @@ -0,0 +1,76 @@ +PowerPlatform.Dataverse.models.fetchxml_query +============================================= + +.. py:module:: PowerPlatform.Dataverse.models.fetchxml_query + +.. autoapi-nested-parse:: + + FetchXmlQuery — inert query object returned by QueryOperations.fetchxml(). + + + +Classes +------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.models.fetchxml_query.FetchXmlQuery + + +Module Contents +--------------- + +.. py:class:: FetchXmlQuery(xml: str, entity_name: str, client: PowerPlatform.Dataverse.client.DataverseClient) + + Inert FetchXML query object. No HTTP request is made until + :meth:`execute` or :meth:`execute_pages` is called. + + Obtained via ``client.query.fetchxml(xml)``. + + :param xml: Stripped, well-formed FetchXML string. + :param entity_name: Entity schema name from the ```` element. + :param client: Parent :class:`~PowerPlatform.Dataverse.client.DataverseClient`. + + + .. py:method:: execute() -> PowerPlatform.Dataverse.models.record.QueryResult + + Execute the FetchXML query and return all results as a :class:`~PowerPlatform.Dataverse.models.record.QueryResult`. + + Blocking — fetches all pages upfront and holds every record in memory before + returning. Simple for small-to-medium result sets; use :meth:`execute_pages` + when the result set may be large or you want to process records as they arrive. + + :return: All matching records across all pages. + :rtype: :class:`~PowerPlatform.Dataverse.models.record.QueryResult` + + Example:: + + rows = client.query.fetchxml(xml).execute() + df = rows.to_dataframe() + + + + .. py:method:: execute_pages() -> Iterator[PowerPlatform.Dataverse.models.record.QueryResult] + + Lazily yield one :class:`~PowerPlatform.Dataverse.models.record.QueryResult` per HTTP page. + + Streaming — each iteration fires one HTTP request and yields one page. + Prefer over :meth:`execute` when: + + - The result set may be large and you do not want all records in memory at once. + - You want early exit: stop iterating once you find what you need and the + remaining HTTP round-trips are skipped automatically. + - You need per-page progress reporting or batched downstream writes. + + One-shot — do not iterate more than once. + + :return: Iterator of per-page :class:`~PowerPlatform.Dataverse.models.record.QueryResult` objects. + :rtype: Iterator[:class:`~PowerPlatform.Dataverse.models.record.QueryResult`] + + Example:: + + for page in client.query.fetchxml(xml).execute_pages(): + process(page.to_dataframe()) + + + diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/filters/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/filters/index.rst.txt new file mode 100644 index 00000000..1e85b977 --- /dev/null +++ b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/filters/index.rst.txt @@ -0,0 +1,365 @@ +PowerPlatform.Dataverse.models.filters +====================================== + +.. py:module:: PowerPlatform.Dataverse.models.filters + +.. autoapi-nested-parse:: + + Composable OData filter expressions for the Dataverse SDK. + + Provides an expression tree that compiles to OData ``$filter`` strings, + with Python operator overloads (``&``, ``|``, ``~``) for composing + complex filter conditions. + + Example:: + + from PowerPlatform.Dataverse.models.filters import col, raw + + # Preferred GA idiom — col() proxy + expr = col("statecode") == 0 + print(expr.to_odata()) # statecode eq 0 + + # Complex composition with OR and AND + expr = (col("statecode") == 0) | (col("statecode") == 1) & (col("revenue") > 100000) + print(expr.to_odata()) + + # In / not-in + expr = col("statecode").in_([0, 1, 2]) + print(expr.to_odata()) + # Microsoft.Dynamics.CRM.In(PropertyName='statecode',PropertyValues=["0","1","2"]) + + # Raw OData escape hatch (no deprecation warning) + expr = raw("Microsoft.Dynamics.CRM.Today(PropertyName='createdon')") + + # Negation + expr = ~(col("statecode") == 1) + print(expr.to_odata()) # not (statecode eq 1) + + + +Classes +------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.models.filters.FilterExpression + PowerPlatform.Dataverse.models.filters.ColumnProxy + + +Functions +--------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.models.filters.col + PowerPlatform.Dataverse.models.filters.raw + PowerPlatform.Dataverse.models.filters.eq + PowerPlatform.Dataverse.models.filters.ne + PowerPlatform.Dataverse.models.filters.gt + PowerPlatform.Dataverse.models.filters.ge + PowerPlatform.Dataverse.models.filters.lt + PowerPlatform.Dataverse.models.filters.le + PowerPlatform.Dataverse.models.filters.contains + PowerPlatform.Dataverse.models.filters.startswith + PowerPlatform.Dataverse.models.filters.endswith + PowerPlatform.Dataverse.models.filters.between + PowerPlatform.Dataverse.models.filters.is_null + PowerPlatform.Dataverse.models.filters.is_not_null + PowerPlatform.Dataverse.models.filters.filter_in + PowerPlatform.Dataverse.models.filters.not_in + PowerPlatform.Dataverse.models.filters.not_between + + +Module Contents +--------------- + +.. py:class:: FilterExpression + + Base class for composable OData filter expressions. + + Supports Python operator overloads for logical composition: + + - ``expr1 & expr2`` produces ``(expr1 and expr2)`` + - ``expr1 | expr2`` produces ``(expr1 or expr2)`` + - ``~expr`` produces ``not (expr)`` + + + .. py:method:: to_odata() -> str + :abstractmethod: + + + Compile this expression to an OData ``$filter`` string. + + + +.. py:class:: ColumnProxy(name: str) + + Fluent proxy for building OData filter expressions from a column name. + + Returned by :func:`col`. Operator overloads and methods produce + :class:`FilterExpression` instances that can be passed to + ``QueryBuilder.where()``. + + Example:: + + from PowerPlatform.Dataverse.models.filters import col + + expr = col("statecode") == 0 # equality + expr = col("revenue") > 1_000_000 # comparison + expr = col("name").like("Contoso%") # startswith + expr = col("name").is_null() # null check + expr = col("statecode").in_([0, 1]) # in + + + .. py:method:: is_null() -> FilterExpression + + Column equals null: ``column eq null``. + + + + .. py:method:: is_not_null() -> FilterExpression + + Column not null: ``column ne null``. + + + + .. py:method:: in_(values: Collection[Any]) -> FilterExpression + + In filter using ``Microsoft.Dynamics.CRM.In``. + + :param values: Non-empty collection of values. + :raises ValueError: If ``values`` is empty. + + + + .. py:method:: not_in(values: Collection[Any]) -> FilterExpression + + Not-in filter using ``Microsoft.Dynamics.CRM.NotIn``. + + :param values: Non-empty collection of values. + :raises ValueError: If ``values`` is empty. + + + + .. py:method:: between(lo: Any, hi: Any) -> FilterExpression + + Between filter: ``(column ge lo and column le hi)``. + + + + .. py:method:: not_between(lo: Any, hi: Any) -> FilterExpression + + Not-between filter: ``not (column ge lo and column le hi)``. + + + + .. py:method:: contains(value: str) -> FilterExpression + + Contains filter: ``contains(column, value)``. + + + + .. py:method:: startswith(value: str) -> FilterExpression + + Startswith filter: ``startswith(column, value)``. + + + + .. py:method:: endswith(value: str) -> FilterExpression + + Endswith filter: ``endswith(column, value)``. + + + + .. py:method:: like(pattern: str) -> FilterExpression + + Pattern-match filter compiled to the closest OData equivalent. + + +-----------------+-----------------------------+-------------------------------------+ + | Pattern form | Example | Compiles to | + +=================+=============================+=====================================+ + | ``val%`` | ``like("Contoso%")`` | ``startswith(column,'Contoso')`` | + +-----------------+-----------------------------+-------------------------------------+ + | ``%val`` | ``like("%Ltd")`` | ``endswith(column,'Ltd')`` | + +-----------------+-----------------------------+-------------------------------------+ + | ``%val%`` | ``like("%Corp%")`` | ``contains(column,'Corp')`` | + +-----------------+-----------------------------+-------------------------------------+ + | No wildcard | ``like("Contoso")`` | ``column eq 'Contoso'`` | + +-----------------+-----------------------------+-------------------------------------+ + | Other | ``like("Con%oso")`` | :class:`ValueError` | + +-----------------+-----------------------------+-------------------------------------+ + + :param pattern: LIKE-style pattern string. + :raises ValueError: If the pattern cannot be reduced to a single OData function. + + + + .. py:method:: not_like(pattern: str) -> FilterExpression + + Negated pattern-match filter; mirrors :meth:`like` rules then negates. + + :param pattern: LIKE-style pattern string (same rules as :meth:`like`). + :raises ValueError: If the pattern cannot be reduced to a single OData function. + + + +.. py:function:: col(name: str) -> ColumnProxy + + Return a :class:`ColumnProxy` for building filter expressions. + + This is the preferred GA idiom for constructing filter expressions:: + + from PowerPlatform.Dataverse.models.filters import col + + expr = col("statecode") == 0 + expr = col("revenue") > 1_000_000 + expr = col("name").like("Contoso%") + expr = col("statecode").in_([0, 1]) + expr = col("parentaccountid").is_null() + + :param name: Column logical name (case-insensitive, will be lowercased). + :return: A :class:`ColumnProxy` bound to the column. + :raises ValueError: If ``name`` is empty. + + +.. py:function:: raw(filter_string: str) -> FilterExpression + + Verbatim OData filter expression (passed through unchanged). + + This function is **not** deprecated — it is the OData escape hatch with + no typed replacement. + + :param filter_string: Raw OData filter string. + :return: A :class:`FilterExpression`. + + Example:: + + raw("Microsoft.Dynamics.CRM.Today(PropertyName='createdon')") + + +.. py:function:: eq(column: str, value: Any) -> FilterExpression + + Equality filter: ``column eq value``. + + .. deprecated:: + Use ``col(column) == value`` instead. + + +.. py:function:: ne(column: str, value: Any) -> FilterExpression + + Not-equal filter: ``column ne value``. + + .. deprecated:: + Use ``col(column) != value`` instead. + + +.. py:function:: gt(column: str, value: Any) -> FilterExpression + + Greater-than filter: ``column gt value``. + + .. deprecated:: + Use ``col(column) > value`` instead. + + +.. py:function:: ge(column: str, value: Any) -> FilterExpression + + Greater-than-or-equal filter: ``column ge value``. + + .. deprecated:: + Use ``col(column) >= value`` instead. + + +.. py:function:: lt(column: str, value: Any) -> FilterExpression + + Less-than filter: ``column lt value``. + + .. deprecated:: + Use ``col(column) < value`` instead. + + +.. py:function:: le(column: str, value: Any) -> FilterExpression + + Less-than-or-equal filter: ``column le value``. + + .. deprecated:: + Use ``col(column) <= value`` instead. + + +.. py:function:: contains(column: str, value: str) -> FilterExpression + + Contains filter: ``contains(column, value)``. + + .. deprecated:: + Use ``col(column).contains(value)`` instead. + + +.. py:function:: startswith(column: str, value: str) -> FilterExpression + + Startswith filter: ``startswith(column, value)``. + + .. deprecated:: + Use ``col(column).startswith(value)`` instead. + + +.. py:function:: endswith(column: str, value: str) -> FilterExpression + + Endswith filter: ``endswith(column, value)``. + + .. deprecated:: + Use ``col(column).endswith(value)`` instead. + + +.. py:function:: between(column: str, low: Any, high: Any) -> FilterExpression + + Between filter: ``(column ge low and column le high)``. + + .. deprecated:: + Use ``col(column).between(low, high)`` instead. + + +.. py:function:: is_null(column: str) -> FilterExpression + + Null check: ``column eq null``. + + .. deprecated:: + Use ``col(column).is_null()`` instead. + + +.. py:function:: is_not_null(column: str) -> FilterExpression + + Not-null check: ``column ne null``. + + .. deprecated:: + Use ``col(column).is_not_null()`` instead. + + +.. py:function:: filter_in(column: str, values: Collection[Any]) -> FilterExpression + + In filter using ``Microsoft.Dynamics.CRM.In``. + + Named ``filter_in`` because ``in`` is a Python keyword. + + .. deprecated:: + Use ``col(column).in_(values)`` instead. + + :raises ValueError: If ``values`` is empty. + + +.. py:function:: not_in(column: str, values: Collection[Any]) -> FilterExpression + + Not-in filter using ``Microsoft.Dynamics.CRM.NotIn``. + + .. deprecated:: + Use ``col(column).not_in(values)`` instead. + + :raises ValueError: If ``values`` is empty. + + +.. py:function:: not_between(column: str, low: Any, high: Any) -> FilterExpression + + Not-between filter: ``not (column ge low and column le high)``. + + .. deprecated:: + Use ``col(column).not_between(low, high)`` instead. + + diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/index.rst.txt new file mode 100644 index 00000000..b7ae9520 --- /dev/null +++ b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/index.rst.txt @@ -0,0 +1,41 @@ +PowerPlatform.Dataverse.models +============================== + +.. py:module:: PowerPlatform.Dataverse.models + +.. autoapi-nested-parse:: + + Data models and type definitions for the Dataverse SDK. + + Provides dataclasses and helpers for Dataverse entities: + + - :class:`~PowerPlatform.Dataverse.models.query_builder.QueryBuilder`: Fluent query builder. + - :mod:`~PowerPlatform.Dataverse.models.filters`: Composable OData filter expressions + via :func:`~PowerPlatform.Dataverse.models.filters.col` and + :func:`~PowerPlatform.Dataverse.models.filters.raw`. + - :class:`~PowerPlatform.Dataverse.models.record.QueryResult`: Iterable result wrapper. + - :class:`~PowerPlatform.Dataverse.models.record.Record`: Dataverse entity record. + - :class:`~PowerPlatform.Dataverse.models.upsert.UpsertItem`: Upsert operation item. + - :class:`~PowerPlatform.Dataverse.models.fetchxml_query.FetchXmlQuery`: FetchXML query object. + - :class:`~PowerPlatform.Dataverse.models.protocol.DataverseModel`: Typed-model protocol. + + + +Submodules +---------- + +.. toctree:: + :maxdepth: 1 + + /autoapi/PowerPlatform/Dataverse/models/batch/index + /autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index + /autoapi/PowerPlatform/Dataverse/models/filters/index + /autoapi/PowerPlatform/Dataverse/models/labels/index + /autoapi/PowerPlatform/Dataverse/models/protocol/index + /autoapi/PowerPlatform/Dataverse/models/query_builder/index + /autoapi/PowerPlatform/Dataverse/models/record/index + /autoapi/PowerPlatform/Dataverse/models/relationship/index + /autoapi/PowerPlatform/Dataverse/models/table_info/index + /autoapi/PowerPlatform/Dataverse/models/upsert/index + + diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/labels/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/labels/index.rst.txt new file mode 100644 index 00000000..f2df2a89 --- /dev/null +++ b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/labels/index.rst.txt @@ -0,0 +1,113 @@ +PowerPlatform.Dataverse.models.labels +===================================== + +.. py:module:: PowerPlatform.Dataverse.models.labels + +.. autoapi-nested-parse:: + + Label models for Dataverse metadata. + + + +Classes +------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.models.labels.LocalizedLabel + PowerPlatform.Dataverse.models.labels.Label + + +Module Contents +--------------- + +.. py:class:: LocalizedLabel + + Represents a localized label with a language code. + + :param label: The text of the label. + :type label: str + :param language_code: The language code (LCID), e.g., 1033 for English. + :type language_code: int + :param additional_properties: Optional dict of additional properties to include + in the Web API payload. These are merged last and can override default values. + :type additional_properties: Optional[Dict[str, Any]] + + + .. py:attribute:: label + :type: str + + + .. py:attribute:: language_code + :type: int + + + .. py:attribute:: additional_properties + :type: Optional[Dict[str, Any]] + :value: None + + + + .. py:method:: to_dict() -> Dict[str, Any] + + Convert to Web API JSON format. + + Example:: + + >>> label = LocalizedLabel(label="Account", language_code=1033) + >>> label.to_dict() + { + '@odata.type': 'Microsoft.Dynamics.CRM.LocalizedLabel', + 'Label': 'Account', + 'LanguageCode': 1033 + } + + + +.. py:class:: Label + + Represents a label that can have multiple localized versions. + + :param localized_labels: List of LocalizedLabel instances. + :type localized_labels: List[LocalizedLabel] + :param user_localized_label: Optional user-specific localized label. + :type user_localized_label: Optional[LocalizedLabel] + :param additional_properties: Optional dict of additional properties to include + in the Web API payload. These are merged last and can override default values. + :type additional_properties: Optional[Dict[str, Any]] + + + .. py:attribute:: localized_labels + :type: List[LocalizedLabel] + + + .. py:attribute:: user_localized_label + :type: Optional[LocalizedLabel] + :value: None + + + + .. py:attribute:: additional_properties + :type: Optional[Dict[str, Any]] + :value: None + + + + .. py:method:: to_dict() -> Dict[str, Any] + + Convert to Web API JSON format. + + Example:: + + >>> label = Label(localized_labels=[LocalizedLabel("Account", 1033)]) + >>> label.to_dict() + { + '@odata.type': 'Microsoft.Dynamics.CRM.Label', + 'LocalizedLabels': [ + {'@odata.type': '...', 'Label': 'Account', 'LanguageCode': 1033} + ], + 'UserLocalizedLabel': {'@odata.type': '...', 'Label': 'Account', ...} + } + + + diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/protocol/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/protocol/index.rst.txt new file mode 100644 index 00000000..03a4fc46 --- /dev/null +++ b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/protocol/index.rst.txt @@ -0,0 +1,94 @@ +PowerPlatform.Dataverse.models.protocol +======================================= + +.. py:module:: PowerPlatform.Dataverse.models.protocol + +.. autoapi-nested-parse:: + + DataverseModel structural Protocol for typed entity integration. + + + +Classes +------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.models.protocol.DataverseModel + + +Module Contents +--------------- + +.. py:class:: DataverseModel + + Bases: :py:obj:`Protocol` + + + Structural Protocol enabling typed entity instances to be passed to + ``records.create()`` and ``records.update()``. + + Implement this Protocol on any entity class (dataclass, Pydantic model, + hand-rolled) to enable it to be passed directly to CRUD operations without + specifying the table name or converting to dict manually. + + Required class variables: + + - ``__entity_logical_name__`` — Dataverse logical entity name (e.g. ``"account"``) + - ``__entity_set_name__`` — OData entity set name (e.g. ``"accounts"``) + + Required instance methods: + + - ``to_dict()`` — return record payload as ``dict`` + - ``from_dict(data)`` — classmethod to reconstruct from a response ``dict`` + + Example:: + + from dataclasses import dataclass + from PowerPlatform.Dataverse import DataverseModel + + @dataclass + class Account: + __entity_logical_name__ = "account" + __entity_set_name__ = "accounts" + name: str = "" + telephone1: str = "" + + def to_dict(self) -> dict: + return {"name": self.name, "telephone1": self.telephone1} + + @classmethod + def from_dict(cls, data: dict) -> "Account": + return cls( + name=data.get("name", ""), + telephone1=data.get("telephone1", ""), + ) + + # isinstance() works today — Protocol is runtime_checkable: + assert isinstance(Account(), DataverseModel) + + # Type your own helpers against the Protocol now: + def save(entity: DataverseModel) -> None: + data = entity.to_dict() + client.records.create(entity.__entity_logical_name__, data) + + .. note:: + + Direct dispatch (``client.records.create(entity)`` without a table name + or dict) is not yet supported and will be added in a future release. + + + .. py:method:: to_dict() -> dict + + Return the record payload as a plain dictionary. + + + + .. py:method:: from_dict(data: dict) -> DataverseModel + :classmethod: + + + Reconstruct an instance from a response dictionary. + + + diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/query_builder/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/query_builder/index.rst.txt new file mode 100644 index 00000000..9e8a9583 --- /dev/null +++ b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/query_builder/index.rst.txt @@ -0,0 +1,332 @@ +PowerPlatform.Dataverse.models.query_builder +============================================ + +.. py:module:: PowerPlatform.Dataverse.models.query_builder + +.. autoapi-nested-parse:: + + Fluent query builder for constructing OData queries. + + Provides a type-safe, discoverable interface for building complex queries + against Dataverse tables with method chaining. + + Example:: + + # Via client (recommended) -- flat iteration over records + from PowerPlatform.Dataverse.models import col + + for record in (client.query.builder("account") + .select("name", "revenue") + .where(col("statecode") == 0) + .where(col("revenue") > 1_000_000) + .order_by("revenue", descending=True) + .top(100) + .execute()): + print(record["name"]) + + # With composable expression tree + from PowerPlatform.Dataverse.models import col, raw + + for record in (client.query.builder("account") + .select("name", "revenue") + .where((col("statecode") == 0) | (col("statecode") == 1)) + .where(col("revenue") > 100000) + .top(100) + .execute()): + print(record["name"]) + + # Lazy paged iteration (one QueryResult per HTTP page) + for page in (client.query.builder("account") + .select("name") + .execute_pages()): + process_batch(page) + + # Get results as a pandas DataFrame + df = (client.query.builder("account") + .select("name", "telephone1") + .where(col("statecode") == 0) + .top(100) + .execute() + .to_dataframe()) + + + +Classes +------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.models.query_builder.QueryParams + PowerPlatform.Dataverse.models.query_builder.ExpandOption + PowerPlatform.Dataverse.models.query_builder.QueryBuilder + + +Module Contents +--------------- + +.. py:class:: QueryParams + + Bases: :py:obj:`TypedDict` + + + Typed dictionary returned by ``QueryBuilder.build()``. + + Provides IDE autocomplete when passing build results to + ``client.records.list()`` manually. + + Initialize self. See help(type(self)) for accurate signature. + + + .. py:attribute:: table + :type: str + + + .. py:attribute:: select + :type: List[str] + + + .. py:attribute:: filter + :type: str + + + .. py:attribute:: orderby + :type: List[str] + + + .. py:attribute:: expand + :type: List[str] + + + .. py:attribute:: top + :type: int + + + .. py:attribute:: page_size + :type: int + + + .. py:attribute:: count + :type: bool + + + .. py:attribute:: include_annotations + :type: str + + +.. py:class:: ExpandOption(relation: str) + + Structured options for an ``$expand`` navigation property. + + Allows specifying nested ``$select``, ``$filter``, ``$orderby``, and + ``$top`` options for a single navigation property expansion, following + the OData ``$expand`` syntax. + + :param relation: Navigation property name (case-sensitive). + :type relation: str + + Example:: + + # Expand Account_Tasks with nested options + opt = (ExpandOption("Account_Tasks") + .select("subject", "createdon") + .filter("contains(subject,'Task')") + .order_by("createdon", descending=True) + .top(5)) + + query = (client.query.builder("account") + .select("name") + .expand(opt) + .execute()) + + + .. py:attribute:: relation + + + .. py:method:: select(*columns: str) -> ExpandOption + + Select specific columns from the expanded entity. + + :param columns: Column names to select. + :return: Self for method chaining. + + + + .. py:method:: filter(filter_str: str) -> ExpandOption + + Filter the expanded collection. + + :param filter_str: OData ``$filter`` expression. + :return: Self for method chaining. + + + + .. py:method:: order_by(column: str, descending: bool = False) -> ExpandOption + + Sort the expanded collection. + + :param column: Column name to sort by. + :param descending: Sort descending if ``True``. + :return: Self for method chaining. + + + + .. py:method:: top(count: int) -> ExpandOption + + Limit expanded results. + + :param count: Maximum number of expanded records. + :return: Self for method chaining. + + + + .. py:method:: to_odata() -> str + + Compile to OData ``$expand`` syntax. + + :return: OData expand string like ``"Nav($select=col1,col2;$filter=...)"`` + :rtype: str + + + +.. py:class:: QueryBuilder(table: str) + + Bases: :py:obj:`_QueryBuilderBase` + + + Fluent interface for building and executing OData queries against a sync client. + + Provides method chaining for constructing complex queries with + composable filter expressions. Can be used standalone (via ``build()``) + or bound to a client (via :meth:`execute`). + + :param table: Table schema name to query. + :type table: str + :raises ValueError: If ``table`` is empty. + + .. rubric:: Example + + Standalone query construction:: + + from PowerPlatform.Dataverse.models import col + + query = (QueryBuilder("account") + .select("name") + .where(col("statecode") == 0) + .top(10)) + params = query.build() + # {"table": "account", "select": ["name"], + # "filter": "statecode eq 0", "top": 10} + + + .. py:method:: execute(*, by_page=_BY_PAGE_UNSET) -> Union[PowerPlatform.Dataverse.models.record.QueryResult, Iterator[PowerPlatform.Dataverse.models.record.QueryResult]] + + Execute the query and return results. + + Returns a :class:`~PowerPlatform.Dataverse.models.record.QueryResult` + with all pages collected. Use :meth:`execute_pages` for lazy per-page + iteration. + + This method is only available when the QueryBuilder was created + via ``client.query.builder(table)``. Standalone ``QueryBuilder`` + instances should use ``build()`` to get parameters and pass them + to ``client.records.list()`` manually. + + At least one of ``select()``, ``where()``, or ``top()`` must be + called before ``execute()``; otherwise a :class:`ValueError` is + raised to prevent accidental full-table scans. + + .. deprecated:: + The ``by_page`` parameter is deprecated. Use :meth:`execute_pages` + for lazy per-page iteration, or plain ``execute()`` (no flag) for + the default eager result. + + :return: :class:`~PowerPlatform.Dataverse.models.record.QueryResult` + with all pages collected (default), or page iterator (deprecated + ``by_page=True``). + :rtype: QueryResult or Iterator[QueryResult] + :raises ValueError: If no ``select``, ``where``, or ``top`` + constraint has been set. + :raises RuntimeError: If the query was not created via + ``client.query.builder()``. + + Example:: + + from PowerPlatform.Dataverse.models import col + + for record in (client.query.builder("account") + .select("name") + .where(col("statecode") == 0) + .execute()): + print(record["name"]) + + + + .. py:method:: execute_pages() -> Iterator[PowerPlatform.Dataverse.models.record.QueryResult] + + Lazily yield one :class:`~PowerPlatform.Dataverse.models.record.QueryResult` + per HTTP page. + + Each iteration triggers a network request via ``@odata.nextLink``. + One-shot — do not iterate more than once. + + At least one of ``select()``, ``where()``, or ``top()`` must be + called before ``execute_pages()``; otherwise a :class:`ValueError` is + raised to prevent accidental full-table scans. + + :return: Iterator of per-page :class:`~PowerPlatform.Dataverse.models.record.QueryResult`. + :rtype: Iterator[QueryResult] + :raises ValueError: If no ``select``, ``where``, or ``top`` + constraint has been set. + :raises RuntimeError: If the query was not created via + ``client.query.builder()``. + + Example:: + + from PowerPlatform.Dataverse.models import col + + for page in (client.query.builder("account") + .select("name") + .where(col("statecode") == 0) + .execute_pages()): + process(page.to_dataframe()) + + + + .. py:method:: to_dataframe() -> pandas.DataFrame + + Execute the query and return results as a pandas DataFrame. + + .. deprecated:: + Use ``QueryBuilder.execute().to_dataframe()`` instead. + ``QueryBuilder.to_dataframe()`` will be removed in a future release. + + All pages are consolidated into a single DataFrame. + + This method is only available when the QueryBuilder was created + via ``client.query.builder(table)``. + + At least one of ``select()``, ``where()``, or ``top()`` must be + called before ``to_dataframe()``; otherwise a :class:`ValueError` + is raised to prevent accidental full-table scans. + + :return: DataFrame containing all matching records. Returns an empty + DataFrame when no records match. + :rtype: ~pandas.DataFrame + :raises ValueError: If no ``select``, ``where``, or ``top`` + constraint has been set. + :raises RuntimeError: If the query was not created via + ``client.query.builder()``. + + Example:: + + from PowerPlatform.Dataverse.models import col + + df = (client.query.builder("account") + .select("name", "telephone1") + .where(col("statecode") == 0) + .top(100) + .execute() + .to_dataframe()) + + + diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/record/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/record/index.rst.txt new file mode 100644 index 00000000..d92274ed --- /dev/null +++ b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/record/index.rst.txt @@ -0,0 +1,152 @@ +PowerPlatform.Dataverse.models.record +===================================== + +.. py:module:: PowerPlatform.Dataverse.models.record + +.. autoapi-nested-parse:: + + Record data model for Dataverse entities. + + + +Classes +------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.models.record.Record + PowerPlatform.Dataverse.models.record.QueryResult + + +Module Contents +--------------- + +.. py:class:: Record + + Strongly-typed Dataverse record with dict-like backward compatibility. + + Wraps raw OData response data into a structured object while preserving + ``result["key"]`` access patterns for existing code. + + :param id: Record GUID. Empty string if not extracted (e.g. paginated + results, SQL queries). + :type id: :class:`str` + :param table: Table schema name used in the request. + :type table: :class:`str` + :param data: Record field data as key-value pairs. + :type data: :class:`dict` + :param etag: ETag for optimistic concurrency, extracted from + ``@odata.etag`` in the API response. + :type etag: :class:`str` or None + + Example:: + + record = client.records.get("account", account_id, select=["name"]) + print(record.id) # structured access + print(record["name"]) # dict-like access (backward compat) + + + .. py:attribute:: id + :type: str + :value: '' + + + + .. py:attribute:: table + :type: str + :value: '' + + + + .. py:attribute:: data + :type: Dict[str, Any] + + + .. py:attribute:: etag + :type: Optional[str] + :value: None + + + + .. py:method:: get(key: str, default: Any = None) -> Any + + Return value for *key*, or *default* if not present. + + + + .. py:method:: keys() -> KeysView[str] + + Return data keys. + + + + .. py:method:: values() -> ValuesView[Any] + + Return data values. + + + + .. py:method:: items() -> ItemsView[str, Any] + + Return data items. + + + + .. py:method:: from_api_response(table: str, response_data: Dict[str, Any], *, record_id: str = '') -> Record + :classmethod: + + + Create a :class:`Record` from a raw OData API response. + + Strips ``@odata.*`` annotation keys from the data and extracts the + ``@odata.etag`` value if present. + + :param table: Table schema name. + :type table: :class:`str` + :param response_data: Raw JSON dict from the OData response. + :type response_data: :class:`dict` + :param record_id: Known record GUID. Pass explicitly when available + (e.g. single-record get). Defaults to empty string. + :type record_id: :class:`str` + :rtype: :class:`Record` + + + + .. py:method:: to_dict() -> Dict[str, Any] + + Return a plain dict copy of the record data (excludes metadata). + + + +.. py:class:: QueryResult(records: List[Record]) + + Iterable wrapper around a list of :class:`Record` objects. + + Returned by :meth:`~PowerPlatform.Dataverse.models.query_builder.QueryBuilder.execute` + (flat mode) and :meth:`~PowerPlatform.Dataverse.operations.records.RecordOperations.list`. + + Backward-compatible: ``for r in result`` continues to work without change. + + :param records: Collected records from all pages. + :type records: list[:class:`Record`] + + + .. py:attribute:: records + :type: List[Record] + + + .. py:method:: first() -> Optional[Record] + + Return the first record, or ``None`` if the result is empty. + + + + .. py:method:: to_dataframe() -> Any + + Return all records as a pandas DataFrame. + + :raises ImportError: If pandas is not installed. + :rtype: ~pandas.DataFrame + + + diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/relationship/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/relationship/index.rst.txt new file mode 100644 index 00000000..06160b7a --- /dev/null +++ b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/relationship/index.rst.txt @@ -0,0 +1,471 @@ +PowerPlatform.Dataverse.models.relationship +=========================================== + +.. py:module:: PowerPlatform.Dataverse.models.relationship + +.. autoapi-nested-parse:: + + Relationship models for Dataverse (input and output). + + + +Classes +------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.models.relationship.CascadeConfiguration + PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata + PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata + PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata + PowerPlatform.Dataverse.models.relationship.RelationshipInfo + + +Module Contents +--------------- + +.. py:class:: CascadeConfiguration + + Defines cascade behavior for relationship operations. + + :param assign: Cascade behavior for assign operations. + :type assign: str + :param delete: Cascade behavior for delete operations. + :type delete: str + :param merge: Cascade behavior for merge operations. + :type merge: str + :param reparent: Cascade behavior for reparent operations. + :type reparent: str + :param share: Cascade behavior for share operations. + :type share: str + :param unshare: Cascade behavior for unshare operations. + :type unshare: str + :param additional_properties: Optional dict of additional properties to include + in the Web API payload (e.g., "Archive", "RollupView"). These are merged + last and can override default values. + :type additional_properties: Optional[Dict[str, Any]] + + Valid values for each parameter: + - "Cascade": Perform the operation on all related records + - "NoCascade": Do not perform the operation on related records + - "RemoveLink": Remove the relationship link but keep the records + - "Restrict": Prevent the operation if related records exist + + + .. py:attribute:: assign + :type: str + :value: 'NoCascade' + + + + .. py:attribute:: delete + :type: str + :value: 'RemoveLink' + + + + .. py:attribute:: merge + :type: str + :value: 'NoCascade' + + + + .. py:attribute:: reparent + :type: str + :value: 'NoCascade' + + + + .. py:attribute:: share + :type: str + :value: 'NoCascade' + + + + .. py:attribute:: unshare + :type: str + :value: 'NoCascade' + + + + .. py:attribute:: additional_properties + :type: Optional[Dict[str, Any]] + :value: None + + + + .. py:method:: to_dict() -> Dict[str, Any] + + Convert to Web API JSON format. + + Example:: + + >>> config = CascadeConfiguration(delete="Cascade", assign="NoCascade") + >>> config.to_dict() + { + 'Assign': 'NoCascade', + 'Delete': 'Cascade', + 'Merge': 'NoCascade', + 'Reparent': 'NoCascade', + 'Share': 'NoCascade', + 'Unshare': 'NoCascade' + } + + + +.. py:class:: LookupAttributeMetadata + + Metadata for a lookup attribute. + + :param schema_name: Schema name for the attribute (e.g., "new_AccountId"). + :type schema_name: str + :param display_name: Display name for the attribute. + :type display_name: Label + :param description: Optional description of the attribute. + :type description: Optional[Label] + :param required_level: Requirement level for the attribute. + :type required_level: str + :param additional_properties: Optional dict of additional properties to include + in the Web API payload. Useful for setting properties like "Targets" (to + specify which entity types the lookup can reference), "LogicalName", + "IsSecured", "IsValidForAdvancedFind", etc. These are merged last and + can override default values. + :type additional_properties: Optional[Dict[str, Any]] + + Valid required_level values: + - "None": The attribute is optional + - "Recommended": The attribute is recommended + - "ApplicationRequired": The attribute is required + + + .. py:attribute:: schema_name + :type: str + + + .. py:attribute:: display_name + :type: PowerPlatform.Dataverse.models.labels.Label + + + .. py:attribute:: description + :type: Optional[PowerPlatform.Dataverse.models.labels.Label] + :value: None + + + + .. py:attribute:: required_level + :type: str + :value: 'None' + + + + .. py:attribute:: additional_properties + :type: Optional[Dict[str, Any]] + :value: None + + + + .. py:method:: to_dict() -> Dict[str, Any] + + Convert to Web API JSON format. + + Example:: + + >>> lookup = LookupAttributeMetadata( + ... schema_name="new_AccountId", + ... display_name=Label([LocalizedLabel("Account", 1033)]) + ... ) + >>> lookup.to_dict() + { + '@odata.type': 'Microsoft.Dynamics.CRM.LookupAttributeMetadata', + 'SchemaName': 'new_AccountId', + 'AttributeType': 'Lookup', + 'AttributeTypeName': {'Value': 'LookupType'}, + 'DisplayName': {...}, + 'RequiredLevel': {'Value': 'None', 'CanBeChanged': True, ...} + } + + + +.. py:class:: OneToManyRelationshipMetadata + + Metadata for a one-to-many entity relationship. + + :param schema_name: Schema name for the relationship (e.g., "new_Account_Orders"). + :type schema_name: str + :param referenced_entity: Logical name of the referenced (parent) entity. + :type referenced_entity: str + :param referencing_entity: Logical name of the referencing (child) entity. + :type referencing_entity: str + :param referenced_attribute: Attribute on the referenced entity (typically the primary key). + :type referenced_attribute: str + :param cascade_configuration: Cascade behavior configuration. + :type cascade_configuration: CascadeConfiguration + :param referencing_attribute: Optional name for the referencing attribute (usually auto-generated). + :type referencing_attribute: Optional[str] + :param additional_properties: Optional dict of additional properties to include + in the Web API payload. Useful for setting inherited properties like + "IsValidForAdvancedFind", "IsCustomizable", "SecurityTypes", etc. + These are merged last and can override default values. + :type additional_properties: Optional[Dict[str, Any]] + + + .. py:attribute:: schema_name + :type: str + + + .. py:attribute:: referenced_entity + :type: str + + + .. py:attribute:: referencing_entity + :type: str + + + .. py:attribute:: referenced_attribute + :type: str + + + .. py:attribute:: cascade_configuration + :type: CascadeConfiguration + + + .. py:attribute:: referencing_attribute + :type: Optional[str] + :value: None + + + + .. py:attribute:: additional_properties + :type: Optional[Dict[str, Any]] + :value: None + + + + .. py:method:: to_dict() -> Dict[str, Any] + + Convert to Web API JSON format. + + Example:: + + >>> rel = OneToManyRelationshipMetadata( + ... schema_name="new_account_orders", + ... referenced_entity="account", + ... referencing_entity="new_order", + ... referenced_attribute="accountid" + ... ) + >>> rel.to_dict() + { + '@odata.type': 'Microsoft.Dynamics.CRM.OneToManyRelationshipMetadata', + 'SchemaName': 'new_account_orders', + 'ReferencedEntity': 'account', + 'ReferencingEntity': 'new_order', + 'ReferencedAttribute': 'accountid', + 'CascadeConfiguration': {...} + } + + + +.. py:class:: ManyToManyRelationshipMetadata + + Metadata for a many-to-many entity relationship. + + :param schema_name: Schema name for the relationship. + :type schema_name: str + :param entity1_logical_name: Logical name of the first entity. + :type entity1_logical_name: str + :param entity2_logical_name: Logical name of the second entity. + :type entity2_logical_name: str + :param intersect_entity_name: Name for the intersect table (defaults to schema_name if not provided). + :type intersect_entity_name: Optional[str] + :param additional_properties: Optional dict of additional properties to include + in the Web API payload. Useful for setting inherited properties like + "IsValidForAdvancedFind", "IsCustomizable", "SecurityTypes", or direct + properties like "Entity1NavigationPropertyName". These are merged last + and can override default values. + :type additional_properties: Optional[Dict[str, Any]] + + + .. py:attribute:: schema_name + :type: str + + + .. py:attribute:: entity1_logical_name + :type: str + + + .. py:attribute:: entity2_logical_name + :type: str + + + .. py:attribute:: intersect_entity_name + :type: Optional[str] + :value: None + + + + .. py:attribute:: additional_properties + :type: Optional[Dict[str, Any]] + :value: None + + + + .. py:method:: to_dict() -> Dict[str, Any] + + Convert to Web API JSON format. + + Example:: + + >>> rel = ManyToManyRelationshipMetadata( + ... schema_name="new_account_contact", + ... entity1_logical_name="account", + ... entity2_logical_name="contact" + ... ) + >>> rel.to_dict() + { + '@odata.type': 'Microsoft.Dynamics.CRM.ManyToManyRelationshipMetadata', + 'SchemaName': 'new_account_contact', + 'Entity1LogicalName': 'account', + 'Entity2LogicalName': 'contact', + 'IntersectEntityName': 'new_account_contact' + } + + + +.. py:class:: RelationshipInfo + + Typed return model for relationship metadata. + + Returned by :meth:`~PowerPlatform.Dataverse.operations.tables.TableOperations.create_one_to_many_relationship`, + :meth:`~PowerPlatform.Dataverse.operations.tables.TableOperations.create_many_to_many_relationship`, + :meth:`~PowerPlatform.Dataverse.operations.tables.TableOperations.get_relationship`, and + :meth:`~PowerPlatform.Dataverse.operations.tables.TableOperations.create_lookup_field`. + + :param relationship_id: Relationship metadata GUID. + :type relationship_id: :class:`str` or None + :param relationship_schema_name: Relationship schema name. + :type relationship_schema_name: :class:`str` + :param relationship_type: Either ``"one_to_many"`` or ``"many_to_many"``. + :type relationship_type: :class:`str` + :param lookup_schema_name: Lookup field schema name (one-to-many only). + :type lookup_schema_name: :class:`str` or None + :param referenced_entity: Parent entity logical name (one-to-many only). + :type referenced_entity: :class:`str` or None + :param referencing_entity: Child entity logical name (one-to-many only). + :type referencing_entity: :class:`str` or None + :param entity1_logical_name: First entity logical name (many-to-many only). + :type entity1_logical_name: :class:`str` or None + :param entity2_logical_name: Second entity logical name (many-to-many only). + :type entity2_logical_name: :class:`str` or None + + Example:: + + result = client.tables.create_one_to_many_relationship(lookup, relationship) + print(result.relationship_schema_name) + print(result.lookup_schema_name) + + + .. py:attribute:: relationship_id + :type: Optional[str] + :value: None + + + + .. py:attribute:: relationship_schema_name + :type: str + :value: '' + + + + .. py:attribute:: relationship_type + :type: str + :value: '' + + + + .. py:attribute:: lookup_schema_name + :type: Optional[str] + :value: None + + + + .. py:attribute:: referenced_entity + :type: Optional[str] + :value: None + + + + .. py:attribute:: referencing_entity + :type: Optional[str] + :value: None + + + + .. py:attribute:: entity1_logical_name + :type: Optional[str] + :value: None + + + + .. py:attribute:: entity2_logical_name + :type: Optional[str] + :value: None + + + + .. py:method:: from_one_to_many(*, relationship_id: Optional[str], relationship_schema_name: str, lookup_schema_name: str, referenced_entity: str, referencing_entity: str) -> RelationshipInfo + :classmethod: + + + Create from a one-to-many relationship result. + + :param relationship_id: Relationship metadata GUID. + :type relationship_id: :class:`str` or None + :param relationship_schema_name: Relationship schema name. + :type relationship_schema_name: :class:`str` + :param lookup_schema_name: Lookup field schema name. + :type lookup_schema_name: :class:`str` + :param referenced_entity: Parent entity logical name. + :type referenced_entity: :class:`str` + :param referencing_entity: Child entity logical name. + :type referencing_entity: :class:`str` + :rtype: :class:`RelationshipInfo` + + + + .. py:method:: from_many_to_many(*, relationship_id: Optional[str], relationship_schema_name: str, entity1_logical_name: str, entity2_logical_name: str) -> RelationshipInfo + :classmethod: + + + Create from a many-to-many relationship result. + + :param relationship_id: Relationship metadata GUID. + :type relationship_id: :class:`str` or None + :param relationship_schema_name: Relationship schema name. + :type relationship_schema_name: :class:`str` + :param entity1_logical_name: First entity logical name. + :type entity1_logical_name: :class:`str` + :param entity2_logical_name: Second entity logical name. + :type entity2_logical_name: :class:`str` + :rtype: :class:`RelationshipInfo` + + + + .. py:method:: from_api_response(response_data: Dict[str, Any]) -> RelationshipInfo + :classmethod: + + + Create from a raw Dataverse Web API response. + + Detects one-to-many vs many-to-many from the ``@odata.type`` field + in the response and maps PascalCase keys to snake_case attributes. + Dataverse only supports these two relationship types; an unrecognized + ``@odata.type`` raises :class:`ValueError`. + + :param response_data: Raw relationship metadata from the Web API. + :type response_data: :class:`dict` + :rtype: :class:`RelationshipInfo` + :raises ValueError: If the ``@odata.type`` is not a recognized + relationship type. + + + diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/table_info/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/table_info/index.rst.txt new file mode 100644 index 00000000..fd73d890 --- /dev/null +++ b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/table_info/index.rst.txt @@ -0,0 +1,305 @@ +PowerPlatform.Dataverse.models.table_info +========================================= + +.. py:module:: PowerPlatform.Dataverse.models.table_info + +.. autoapi-nested-parse:: + + Table and column metadata models for Dataverse. + + + +Classes +------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.models.table_info.ColumnInfo + PowerPlatform.Dataverse.models.table_info.TableInfo + PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo + + +Module Contents +--------------- + +.. py:class:: ColumnInfo + + Column metadata from a Dataverse table definition. + + :param schema_name: Column schema name (e.g. ``"new_Price"``). + :type schema_name: :class:`str` + :param logical_name: Column logical name (lowercase). + :type logical_name: :class:`str` + :param type: Column type string (e.g. ``"String"``, ``"Integer"``). + :type type: :class:`str` + :param is_primary: Whether this is the primary name column. + :type is_primary: :class:`bool` + :param is_required: Whether the column is required. + :type is_required: :class:`bool` + :param max_length: Maximum length for string columns. + :type max_length: :class:`int` or None + :param display_name: Human-readable display name. + :type display_name: :class:`str` or None + :param description: Column description. + :type description: :class:`str` or None + + + .. py:attribute:: schema_name + :type: str + :value: '' + + + + .. py:attribute:: logical_name + :type: str + :value: '' + + + + .. py:attribute:: type + :type: str + :value: '' + + + + .. py:attribute:: is_primary + :type: bool + :value: False + + + + .. py:attribute:: is_required + :type: bool + :value: False + + + + .. py:attribute:: max_length + :type: Optional[int] + :value: None + + + + .. py:attribute:: display_name + :type: Optional[str] + :value: None + + + + .. py:attribute:: description + :type: Optional[str] + :value: None + + + + .. py:method:: from_api_response(response_data: Dict[str, Any]) -> ColumnInfo + :classmethod: + + + Create from a raw Dataverse ``AttributeMetadata`` API response. + + :param response_data: Raw attribute metadata dict (PascalCase keys). + :type response_data: :class:`dict` + :rtype: :class:`ColumnInfo` + + + +.. py:class:: TableInfo + + Table metadata with dict-like backward compatibility. + + Supports both new attribute access (``info.schema_name``) and legacy + dict-key access (``info["table_schema_name"]``) for backward + compatibility with code written against the raw dict API. + + :param schema_name: Table schema name (e.g. ``"Account"``). + :type schema_name: :class:`str` + :param logical_name: Table logical name (lowercase). + :type logical_name: :class:`str` + :param entity_set_name: OData entity set name. + :type entity_set_name: :class:`str` + :param metadata_id: Metadata GUID. + :type metadata_id: :class:`str` + :param display_name: Human-readable display name. + :type display_name: :class:`str` or None + :param description: Table description. + :type description: :class:`str` or None + :param columns: Column metadata (when retrieved). + :type columns: list[ColumnInfo] or None + :param columns_created: Column schema names created with the table. + :type columns_created: list[str] or None + + Example:: + + info = client.tables.create("new_Product", {"new_Price": "decimal"}) + print(info.schema_name) # new attribute access + print(info["table_schema_name"]) # legacy dict-key access + + + .. py:attribute:: schema_name + :type: str + :value: '' + + + + .. py:attribute:: logical_name + :type: str + :value: '' + + + + .. py:attribute:: entity_set_name + :type: str + :value: '' + + + + .. py:attribute:: metadata_id + :type: str + :value: '' + + + + .. py:attribute:: primary_name_attribute + :type: Optional[str] + :value: None + + + + .. py:attribute:: primary_id_attribute + :type: Optional[str] + :value: None + + + + .. py:attribute:: display_name + :type: Optional[str] + :value: None + + + + .. py:attribute:: description + :type: Optional[str] + :value: None + + + + .. py:attribute:: columns + :type: Optional[List[ColumnInfo]] + :value: None + + + + .. py:attribute:: columns_created + :type: Optional[List[str]] + :value: None + + + + .. py:method:: get(key: str, default: Any = None) -> Any + + Return value for *key*, or *default* if not present. + + + + .. py:method:: keys() -> KeysView[str] + + Return legacy dict keys. + + + + .. py:method:: values() -> List[Any] + + Return values corresponding to legacy dict keys. + + + + .. py:method:: items() -> List[tuple] + + Return (legacy_key, value) pairs. + + + + .. py:method:: from_dict(data: Dict[str, Any]) -> TableInfo + :classmethod: + + + Create from an SDK internal dict (snake_case keys). + + This handles the dict format returned by ``_create_table`` and + ``_get_table_info`` in the OData layer. + + :param data: Dictionary with SDK snake_case keys. + :type data: :class:`dict` + :rtype: :class:`TableInfo` + + + + .. py:method:: from_api_response(response_data: Dict[str, Any]) -> TableInfo + :classmethod: + + + Create from a raw Dataverse ``EntityDefinition`` API response. + + :param response_data: Raw entity metadata dict (PascalCase keys). + :type response_data: :class:`dict` + :rtype: :class:`TableInfo` + + + + .. py:method:: to_dict() -> Dict[str, Any] + + Return a dict with legacy keys for backward compatibility. + + + +.. py:class:: AlternateKeyInfo + + Alternate key metadata for a Dataverse table. + + :param metadata_id: Key metadata GUID. + :type metadata_id: :class:`str` + :param schema_name: Key schema name. + :type schema_name: :class:`str` + :param key_attributes: List of column logical names that compose the key. + :type key_attributes: list[str] + :param status: Index creation status (``"Active"``, ``"Pending"``, ``"InProgress"``, ``"Failed"``). + :type status: :class:`str` + + + .. py:attribute:: metadata_id + :type: str + :value: '' + + + + .. py:attribute:: schema_name + :type: str + :value: '' + + + + .. py:attribute:: key_attributes + :type: List[str] + :value: [] + + + + .. py:attribute:: status + :type: str + :value: '' + + + + .. py:method:: from_api_response(response_data: Dict[str, Any]) -> AlternateKeyInfo + :classmethod: + + + Create from raw EntityKeyMetadata API response. + + :param response_data: Raw key metadata dictionary from the Web API. + :type response_data: :class:`dict` + :rtype: :class:`AlternateKeyInfo` + + + diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/upsert/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/upsert/index.rst.txt new file mode 100644 index 00000000..b6568676 --- /dev/null +++ b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/upsert/index.rst.txt @@ -0,0 +1,54 @@ +PowerPlatform.Dataverse.models.upsert +===================================== + +.. py:module:: PowerPlatform.Dataverse.models.upsert + +.. autoapi-nested-parse:: + + Upsert data models for the Dataverse SDK. + + + +Classes +------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.models.upsert.UpsertItem + + +Module Contents +--------------- + +.. py:class:: UpsertItem + + Represents a single upsert operation targeting a record by its alternate key. + + Used with :meth:`~PowerPlatform.Dataverse.operations.records.RecordOperations.upsert` + to upsert one or more records identified by alternate keys rather than primary GUIDs. + + :param alternate_key: Dictionary mapping alternate key attribute names to their values. + String values are automatically quoted and escaped in the OData URL. Integer and + other non-string values are included without quotes. + :type alternate_key: dict[str, Any] + :param record: Dictionary of attribute names to values for the record payload. + Keys are automatically lowercased. Picklist labels are resolved to integer option + values when a matching option set is found. + :type record: dict[str, Any] + + Example:: + + item = UpsertItem( + alternate_key={"accountnumber": "ACC-001", "address1_postalcode": "98052"}, + record={"name": "Contoso Ltd", "telephone1": "555-0100"}, + ) + + + .. py:attribute:: alternate_key + :type: Dict[str, Any] + + + .. py:attribute:: record + :type: Dict[str, Any] + + diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/batch/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/batch/index.rst.txt new file mode 100644 index 00000000..160530b7 --- /dev/null +++ b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/batch/index.rst.txt @@ -0,0 +1,741 @@ +PowerPlatform.Dataverse.operations.batch +======================================== + +.. py:module:: PowerPlatform.Dataverse.operations.batch + +.. autoapi-nested-parse:: + + Batch operation namespaces for the Dataverse SDK. + + + +Classes +------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.operations.batch.ChangeSetRecordOperations + PowerPlatform.Dataverse.operations.batch.ChangeSet + PowerPlatform.Dataverse.operations.batch.BatchRecordOperations + PowerPlatform.Dataverse.operations.batch.BatchTableOperations + PowerPlatform.Dataverse.operations.batch.BatchQueryOperations + PowerPlatform.Dataverse.operations.batch.BatchDataFrameOperations + PowerPlatform.Dataverse.operations.batch.BatchRequest + PowerPlatform.Dataverse.operations.batch.BatchOperations + + +Module Contents +--------------- + +.. py:class:: ChangeSetRecordOperations(cs_internal: PowerPlatform.Dataverse.data._batch._ChangeSet) + + Record write operations available inside a :class:`ChangeSet`. + + Mirrors ``client.records`` but restricted to single-record forms (no bulk + create/update/delete). Only write operations are allowed — GET is not + permitted inside a changeset. + + Do not instantiate directly; use ``ChangeSet.records``. + + + .. py:method:: create(table: str, data: Dict[str, Any]) -> str + + Add a single-record create to this changeset. + + :param table: Table schema name (e.g. ``"account"``). + :type table: :class:`str` + :param data: Column values for the new record. + :type data: dict[str, typing.Any] + :returns: A content-ID reference string (e.g. ``"$1"``) usable in + subsequent operations within this changeset as a URI reference + in ``@odata.bind`` fields or as ``record_id`` in + :meth:`update` / :meth:`delete`. + :rtype: :class:`str` + + Example:: + + with batch.changeset() as cs: + lead_ref = cs.records.create("lead", {"firstname": "Ada"}) + cs.records.create("account", { + "name": "Babbage", + "originatingleadid@odata.bind": lead_ref, + }) + + + + .. py:method:: update(table: str, record_id: str, changes: Dict[str, Any]) -> None + + Add a single-record update to this changeset. + + :param table: Table schema name. Ignored when ``record_id`` is a + content-ID reference. + :type table: :class:`str` + :param record_id: GUID or a content-ID reference (e.g. ``"$1"``) + returned by a prior :meth:`create` in this changeset. + :type record_id: :class:`str` + :param changes: Column values to update. + :type changes: dict[str, typing.Any] + + + + .. py:method:: delete(table: str, record_id: str) -> None + + Add a single-record delete to this changeset. + + :param table: Table schema name. Ignored when ``record_id`` is a + content-ID reference. + :type table: :class:`str` + :param record_id: GUID or a content-ID reference (e.g. ``"$1"``). + :type record_id: :class:`str` + + + +.. py:class:: ChangeSet(internal: PowerPlatform.Dataverse.data._batch._ChangeSet) + + A transactional group of single-record write operations. + + All operations succeed or are rolled back together. Use as a context + manager or call ``records`` to add operations directly. + + Do not instantiate directly; use :meth:`BatchRequest.changeset`. + + Example:: + + with batch.changeset() as cs: + ref = cs.records.create("contact", {"firstname": "Alice"}) + cs.records.update("account", account_id, { + "primarycontactid@odata.bind": ref + }) + + + .. py:attribute:: records + + +.. py:class:: BatchRecordOperations(batch: _BatchContext) + + Record operations on a :class:`BatchRequest`. + + Mirrors ``client.records``: same method names, same signatures. + All methods return ``None``; results are available via + :class:`~PowerPlatform.Dataverse.models.batch.BatchResult` after + :meth:`BatchRequest.execute`. + + GA methods: :meth:`retrieve` (single record) and :meth:`list` (multi-record, + single page). :meth:`get` is deprecated — use :meth:`retrieve` instead. + + Do not instantiate directly; use ``batch.records``. + + + .. py:method:: create(table: str, data: Union[Dict[str, Any], List[Dict[str, Any]]]) -> None + + Add a create operation to the batch. + + A single dict creates one record (POST entity_set). + A list of dicts creates all records via the ``CreateMultiple`` action + (one batch item). + + :param table: Table schema name (e.g. ``"account"``). + :type table: :class:`str` + :param data: Single record dict or list of record dicts. + :type data: dict or list[dict] + + + + .. py:method:: update(table: str, ids: Union[str, List[str]], changes: Union[Dict[str, Any], List[Dict[str, Any]]]) -> None + + Add an update operation to the batch. + + - **Single** ``(table, "guid", {...})`` -> one PATCH request. + - **Broadcast** ``(table, [id1, id2], {...})`` -> one ``UpdateMultiple`` POST. + - **Paired** ``(table, [id1, id2], [{...}, {...}])`` -> one ``UpdateMultiple`` POST. + + :param table: Table schema name. + :type table: :class:`str` + :param ids: Single GUID or list of GUIDs. + :type ids: str or list[str] + :param changes: Single dict (single/broadcast) or list of dicts (paired). + :type changes: dict or list[dict] + + + + .. py:method:: delete(table: str, ids: Union[str, List[str]], *, use_bulk_delete: bool = True) -> None + + Add a delete operation to the batch. + + - **Single** ``(table, "guid")`` -> one DELETE request. + - **List + use_bulk_delete=True** (default) -> one ``BulkDelete`` POST. + The async job ID will be available in ``BatchItemResponse.data["JobId"]``. + - **List + use_bulk_delete=False** -> one DELETE per record. + + :param table: Table schema name. + :type table: :class:`str` + :param ids: Single GUID or list of GUIDs. + :type ids: str or list[str] + :param use_bulk_delete: When True (default) and ``ids`` is a list, use the + BulkDelete action. When False, delete records individually. + :type use_bulk_delete: :class:`bool` + + + + .. py:method:: get(table: str, record_id: str, *, select: Optional[List[str]] = None) -> None + + Add a single-record get operation to the batch. + + .. deprecated:: + Use :meth:`retrieve` instead. ``batch.records.get()`` is deprecated + and will be removed in a future release. + + :param table: Table schema name. + :type table: :class:`str` + :param record_id: GUID of the record to retrieve. + :type record_id: :class:`str` + :param select: Optional list of column names to include. + :type select: list[str] or None + + + + .. py:method:: upsert(table: str, items: List[Union[PowerPlatform.Dataverse.models.upsert.UpsertItem, Dict[str, Any]]]) -> None + + Add an upsert operation to the batch. + + Mirrors :meth:`~PowerPlatform.Dataverse.operations.records.RecordOperations.upsert`: + a single item becomes a PATCH request using the alternate key; multiple items + become one ``UpsertMultiple`` POST. + + Each item must be a :class:`~PowerPlatform.Dataverse.models.upsert.UpsertItem` + or a plain ``dict`` with ``"alternate_key"`` and ``"record"`` keys (both dicts). + + :param table: Table schema name (e.g. ``"account"``). + :type table: :class:`str` + :param items: Non-empty list of :class:`~PowerPlatform.Dataverse.models.upsert.UpsertItem` + instances or equivalent dicts. + :type items: list[~PowerPlatform.Dataverse.models.upsert.UpsertItem] + + :raises TypeError: If ``items`` is not a non-empty list, or if any element is + neither a :class:`~PowerPlatform.Dataverse.models.upsert.UpsertItem` nor a + dict with ``"alternate_key"`` and ``"record"`` keys. + + Example:: + + from PowerPlatform.Dataverse.models import UpsertItem + + batch.records.upsert("account", [ + UpsertItem( + alternate_key={"accountnumber": "ACC-001"}, + record={"name": "Contoso Ltd"}, + ), + UpsertItem( + alternate_key={"accountnumber": "ACC-002"}, + record={"name": "Fabrikam Inc"}, + ), + ]) + + + + .. py:method:: retrieve(table: str, record_id: str, *, select: Optional[List[str]] = None, expand: Optional[List[str]] = None, include_annotations: Optional[str] = None) -> None + + Add a single-record retrieve operation to the batch. + + GA replacement for the deprecated :meth:`get`. Enqueues a GET request + for one record by its GUID. The response body will be available in + :attr:`~PowerPlatform.Dataverse.models.batch.BatchItemResponse.data` + after :meth:`BatchRequest.execute`. + + :param table: Table schema name (e.g. ``"account"``). + :type table: :class:`str` + :param record_id: GUID of the record to retrieve. + :type record_id: :class:`str` + :param select: Optional list of column logical names to include. + :type select: list[str] or None + :param expand: Optional list of navigation properties to expand. + Navigation property names are case-sensitive and must match the + entity's ``$metadata``. + :type expand: list[str] or None + :param include_annotations: OData annotation pattern for the + ``Prefer: odata.include-annotations`` header (e.g. ``"*"`` or + ``"OData.Community.Display.V1.FormattedValue"``), or ``None``. + :type include_annotations: :class:`str` or None + + Example:: + + batch = client.batch.new() + batch.records.retrieve( + "account", account_id, + select=["name", "statuscode"], + expand=["primarycontactid"], + include_annotations="OData.Community.Display.V1.FormattedValue", + ) + result = batch.execute() + record = result.responses[0].data + contact = (record.get("primarycontactid") or {}) + print(contact.get("fullname")) + + + + .. py:method:: list(table: str, *, filter: Optional[Union[str, FilterExpression]] = None, select: Optional[List[str]] = None, orderby: Optional[List[str]] = None, top: Optional[int] = None, expand: Optional[List[str]] = None, page_size: Optional[int] = None, count: bool = False, include_annotations: Optional[str] = None) -> None + + Add a multi-record list operation to the batch (single page, no pagination). + + Enqueues a GET request for multiple records. Because batch requests are + a single HTTP round-trip, pagination (``@odata.nextLink``) is **not** + supported — use ``top`` to bound the result size, or rely on the + server's default page limit. + + The response body (``{"value": [...]}`` JSON) will be available in + :attr:`~PowerPlatform.Dataverse.models.batch.BatchItemResponse.data` + after :meth:`BatchRequest.execute`. + + :param table: Table schema name (e.g. ``"account"``). + :type table: :class:`str` + :param filter: Optional OData ``$filter`` expression or :class:`~PowerPlatform.Dataverse.models.filters.FilterExpression`. + :type filter: str or FilterExpression or None + :param select: Optional list of column logical names to include. + :type select: list[str] or None + :param orderby: Optional list of sort expressions (e.g. ``["name asc"]``). + :type orderby: list[str] or None + :param top: Maximum number of records to return. + :type top: int or None + :param expand: Optional list of navigation properties to expand. + :type expand: list[str] or None + :param page_size: Per-page size hint via ``Prefer: odata.maxpagesize``. + :type page_size: int or None + :param count: If ``True``, adds ``$count=true`` to the request. + :type count: bool + :param include_annotations: OData annotation pattern for the + ``Prefer: odata.include-annotations`` header, or ``None``. + :type include_annotations: :class:`str` or None + + Example:: + + batch = client.batch.new() + batch.records.list( + "account", + filter="statecode eq 0", + select=["name", "statuscode"], + orderby=["name asc"], + top=50, + include_annotations="OData.Community.Display.V1.FormattedValue", + ) + result = batch.execute() + records = result.responses[0].data.get("value", []) + + + +.. py:class:: BatchTableOperations(batch: _BatchContext) + + Table metadata operations on a :class:`BatchRequest`. + + Mirrors ``client.tables`` exactly: same method names, same signatures. + All methods return ``None``; results arrive via + :class:`~PowerPlatform.Dataverse.models.batch.BatchResult`. + + .. note:: + ``tables.delete``, ``tables.add_columns``, and ``tables.remove_columns`` + require a metadata lookup (GET ``EntityDefinitions``) at + :meth:`BatchRequest.execute` time to resolve the table's MetadataId. + This lookup is transparent to the caller. + + .. note:: + ``tables.add_columns`` and ``tables.remove_columns`` each produce one + batch item per column, so they contribute multiple entries to + :attr:`~PowerPlatform.Dataverse.models.batch.BatchResult.responses`. + + Do not instantiate directly; use ``batch.tables``. + + + .. py:method:: create(table: str, columns: Dict[str, Any], *, solution: Optional[str] = None, primary_column: Optional[str] = None, display_name: Optional[str] = None) -> None + + Add a table-create operation to the batch. + + .. note:: + The pre-existence check performed by ``client.tables.create`` is skipped + in batch mode. If the table already exists the server returns an error + in the corresponding :class:`~PowerPlatform.Dataverse.models.batch.BatchItemResponse`. + + :param table: Schema name of the new table (e.g. ``"new_Product"``). + :type table: :class:`str` + :param columns: Mapping of column schema names to type strings or Enum subclasses. + :type columns: dict[str, typing.Any] + :param solution: Optional solution unique name. + :type solution: str or None + :param primary_column: Optional primary column schema name. + :type primary_column: str or None + :param display_name: Human-readable display name for the table. + When omitted, defaults to the table schema name. + :type display_name: str or None + + + + .. py:method:: delete(table: str) -> None + + Add a table-delete operation to the batch. + + The table's ``MetadataId`` is resolved via a GET request at execute time. + + :param table: Schema name of the table to delete. + :type table: :class:`str` + + + + .. py:method:: get(table: str) -> None + + Add a table-metadata-get operation to the batch. + + The response will be in ``BatchItemResponse.data`` after execute. + + :param table: Schema name of the table. + :type table: :class:`str` + + + + .. py:method:: list(*, filter: Optional[str] = None, select: Optional[List[str]] = None) -> None + + Add a list-all-tables operation to the batch. + + Mirrors ``client.tables.list()``. Supply an optional OData + ``$filter`` expression to further narrow the results (combined with + ``IsPrivate eq false`` using ``and``). ``select`` projects + specific property names via ``$select``. + + The response will be in ``BatchItemResponse.data`` after execute. + + :param filter: Additional OData ``$filter`` expression. + :type filter: str or None + :param select: List of property names for ``$select``. + :type select: list[str] or None + + + + .. py:method:: add_columns(table: str, columns: Dict[str, Any]) -> None + + Add column-create operations to the batch (one per column). + + The table's ``MetadataId`` is resolved at execute time. Each column + produces one entry in :attr:`~PowerPlatform.Dataverse.models.batch.BatchResult.responses`. + + :param table: Schema name of the target table. + :type table: :class:`str` + :param columns: Mapping of column schema names to type strings or Enum subclasses. + :type columns: dict[str, typing.Any] + + + + .. py:method:: remove_columns(table: str, columns: Union[str, List[str]]) -> None + + Add column-delete operations to the batch (one per column). + + The table's ``MetadataId`` and each column's ``MetadataId`` are resolved + at execute time. Each column produces one entry in + :attr:`~PowerPlatform.Dataverse.models.batch.BatchResult.responses`. + + :param table: Schema name of the target table. + :type table: :class:`str` + :param columns: Column schema name or list of column schema names to remove. + :type columns: str or list[str] + + + + .. py:method:: create_one_to_many_relationship(lookup: PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata, relationship: PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata, *, solution: Optional[str] = None) -> None + + Add a one-to-many relationship creation to the batch. + + :param lookup: Lookup attribute metadata. + :type lookup: ~PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata + :param relationship: Relationship metadata. + :type relationship: ~PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata + :param solution: Optional solution unique name. + :type solution: str or None + + + + .. py:method:: create_many_to_many_relationship(relationship: PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata, *, solution: Optional[str] = None) -> None + + Add a many-to-many relationship creation to the batch. + + :param relationship: Relationship metadata. + :type relationship: ~PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata + :param solution: Optional solution unique name. + :type solution: str or None + + + + .. py:method:: delete_relationship(relationship_id: str) -> None + + Add a relationship-delete operation to the batch. + + :param relationship_id: GUID of the relationship metadata to delete. + :type relationship_id: :class:`str` + + + + .. py:method:: get_relationship(schema_name: str) -> None + + Add a relationship-metadata-get operation to the batch. + + The response will be in ``BatchItemResponse.data`` after execute. + + :param schema_name: Schema name of the relationship. + :type schema_name: :class:`str` + + + + .. py:method:: create_lookup_field(referencing_table: str, lookup_field_name: str, referenced_table: str, *, display_name: Optional[str] = None, description: Optional[str] = None, required: bool = False, cascade_delete: str = CASCADE_BEHAVIOR_REMOVE_LINK, solution: Optional[str] = None, language_code: int = 1033) -> None + + Add a lookup field creation to the batch (convenience wrapper for + :meth:`create_one_to_many_relationship`). + + :param referencing_table: Logical name of the child (many) table. + :type referencing_table: :class:`str` + :param lookup_field_name: Schema name for the lookup field. + :type lookup_field_name: :class:`str` + :param referenced_table: Logical name of the parent (one) table. + :type referenced_table: :class:`str` + :param display_name: Display name for the lookup field. + :type display_name: str or None + :param description: Optional description. + :type description: str or None + :param required: Whether the lookup is required. + :type required: :class:`bool` + :param cascade_delete: Delete cascade behaviour. + :type cascade_delete: :class:`str` + :param solution: Optional solution unique name. + :type solution: str or None + :param language_code: Language code for labels (default 1033). + :type language_code: :class:`int` + + + +.. py:class:: BatchQueryOperations(batch: _BatchContext) + + Query operations on a :class:`BatchRequest`. + + Mirrors ``client.query`` exactly: same method names, same signatures. + All methods return ``None``; results arrive via + :class:`~PowerPlatform.Dataverse.models.batch.BatchResult`. + + Do not instantiate directly; use ``batch.query``. + + + .. py:method:: sql(sql: str) -> None + + Add a SQL SELECT query to the batch. + + Mirrors :meth:`~PowerPlatform.Dataverse.operations.query.QueryOperations.sql`. + The entity set is resolved from the table name in the SQL statement at + :meth:`BatchRequest.execute` time. + + :param sql: A single ``SELECT`` statement within the Dataverse-supported subset. + :type sql: :class:`str` + + :raises ~PowerPlatform.Dataverse.core.errors.ValidationError: + If ``sql`` is not a non-empty string. + + Example:: + + batch.query.sql("SELECT accountid, name FROM account WHERE name = 'Contoso'") + + + +.. py:class:: BatchDataFrameOperations(batch: _BatchContext) + + DataFrame-oriented wrappers for batch record operations. + + Provides :meth:`create`, :meth:`update`, and :meth:`delete` that accept + ``pandas.DataFrame`` / ``pandas.Series`` inputs and convert them to standard + dicts before enqueueing on the batch. This lets data-science callers feed + DataFrames directly into a batch without manual conversion. + + Accessed via ``batch.dataframe``. + + Example:: + + import pandas as pd + + batch = client.batch.new() + df = pd.DataFrame([ + {"name": "Contoso", "telephone1": "555-0100"}, + {"name": "Fabrikam", "telephone1": "555-0200"}, + ]) + batch.dataframe.create("account", df) + result = batch.execute() + + + .. py:method:: create(table: str, records: pandas.DataFrame) -> None + + Enqueue record creates from a pandas DataFrame. + + Each row becomes a record. All rows are bundled in a single + ``CreateMultiple`` batch item (one HTTP request in the batch). + + :param table: Table schema name (e.g. ``"account"``). + :type table: :class:`str` + :param records: DataFrame where each row is a record to create. + :type records: ~pandas.DataFrame + + :raises TypeError: If ``records`` is not a pandas DataFrame. + :raises ValueError: If ``records`` is empty or any row has no non-null values. + + Example:: + + df = pd.DataFrame([{"name": "Contoso"}, {"name": "Fabrikam"}]) + batch.dataframe.create("account", df) + + + + .. py:method:: update(table: str, changes: pandas.DataFrame, id_column: str, clear_nulls: bool = False) -> None + + Enqueue record updates from a pandas DataFrame. + + Each row represents an update. The ``id_column`` specifies which + column contains the record GUIDs. + + :param table: Table schema name (e.g. ``"account"``). + :type table: :class:`str` + :param changes: DataFrame where each row contains a record GUID and + the fields to update. + :type changes: ~pandas.DataFrame + :param id_column: Name of the DataFrame column containing record GUIDs. + :type id_column: :class:`str` + :param clear_nulls: When ``False`` (default), NaN/None values are + skipped. When ``True``, NaN/None sends ``null`` to clear the field. + :type clear_nulls: :class:`bool` + + :raises TypeError: If ``changes`` is not a pandas DataFrame. + :raises ValueError: If ``changes`` is empty, ``id_column`` is missing, + or IDs are invalid. + + Example:: + + df = pd.DataFrame([ + {"accountid": "guid-1", "telephone1": "555-0100"}, + {"accountid": "guid-2", "telephone1": "555-0200"}, + ]) + batch.dataframe.update("account", df, id_column="accountid") + + + + .. py:method:: delete(table: str, ids: pandas.Series, use_bulk_delete: bool = True) -> None + + Enqueue record deletes from a pandas Series of GUIDs. + + :param table: Table schema name (e.g. ``"account"``). + :type table: :class:`str` + :param ids: Series of record GUIDs to delete. + :type ids: ~pandas.Series + :param use_bulk_delete: When ``True`` (default) and ``ids`` has multiple + values, use the ``BulkDelete`` action. + :type use_bulk_delete: :class:`bool` + + :raises TypeError: If ``ids`` is not a pandas Series. + :raises ValueError: If ``ids`` contains invalid values. + + Example:: + + ids_series = pd.Series(["guid-1", "guid-2", "guid-3"]) + batch.dataframe.delete("account", ids_series) + + + +.. py:class:: BatchRequest(client: PowerPlatform.Dataverse.client.DataverseClient) + + Builder for constructing and executing a Dataverse OData ``$batch`` request. + + Obtain via :meth:`BatchOperations.new` (``client.batch.new()``). Add operations + through ``records``, ``tables``, ``query``, and ``dataframe``, + optionally group writes + into a :meth:`changeset`, then call :meth:`execute`. + + Operations are executed sequentially in the order added. The resulting + :class:`~PowerPlatform.Dataverse.models.batch.BatchResult` contains one + :class:`~PowerPlatform.Dataverse.models.batch.BatchItemResponse` per HTTP + request dispatched (some operations expand to multiple requests). + + .. note:: + Maximum 1000 HTTP operations per batch. + + Example:: + + batch = client.batch.new() + batch.records.create("account", {"name": "Contoso"}) + batch.tables.get("account") + with batch.changeset() as cs: + ref = cs.records.create("contact", {"firstname": "Alice"}) + cs.records.update("account", account_id, { + "primarycontactid@odata.bind": ref + }) + result = batch.execute() + + + .. py:attribute:: records + + + .. py:attribute:: tables + + + .. py:attribute:: query + + + .. py:attribute:: dataframe + + + .. py:method:: changeset() -> ChangeSet + + Create a new :class:`ChangeSet` attached to this batch. + + The changeset is added to the batch immediately. Operations added to + the returned :class:`ChangeSet` via ``cs.records.*`` execute atomically. + + :returns: A new :class:`ChangeSet` ready to receive operations. + + Example:: + + with batch.changeset() as cs: + cs.records.create("account", {"name": "ACME"}) + cs.records.create("contact", {"firstname": "Bob"}) + + + + .. py:method:: execute(*, continue_on_error: bool = False) -> PowerPlatform.Dataverse.models.batch.BatchResult + + Submit the batch to Dataverse and return all responses. + + :param continue_on_error: When False (default), Dataverse stops at the + first failure and returns that operation's error as a 4xx response. + When True, ``Prefer: odata.continue-on-error`` is sent and all + operations are attempted. + :returns: :class:`~PowerPlatform.Dataverse.models.batch.BatchResult` + with one entry per HTTP operation in submission order. + :raises ValidationError: If the batch exceeds 1000 operations or an + unsupported column type is specified. + :raises MetadataError: If metadata pre-resolution fails (table or + column not found) for ``tables.delete``, ``tables.add_columns``, + or ``tables.remove_columns``. + :raises HttpError: On HTTP-level failures (auth, server error, etc.) + that prevent the batch from executing. + + + +.. py:class:: BatchOperations(client: PowerPlatform.Dataverse.client.DataverseClient) + + Namespace for batch operations (``client.batch``). + + Accessed via ``client.batch``. Use :meth:`new` to create a + :class:`BatchRequest` builder. + + :param client: The parent :class:`~PowerPlatform.Dataverse.client.DataverseClient` instance. + + Example:: + + batch = client.batch.new() + batch.records.create("account", {"name": "Fabrikam"}) + result = batch.execute() + + + .. py:method:: new() -> BatchRequest + + Create a new empty :class:`BatchRequest` builder. + + :returns: An empty :class:`BatchRequest`. + + + diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/dataframe/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/dataframe/index.rst.txt new file mode 100644 index 00000000..d240dee2 --- /dev/null +++ b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/dataframe/index.rst.txt @@ -0,0 +1,279 @@ +PowerPlatform.Dataverse.operations.dataframe +============================================ + +.. py:module:: PowerPlatform.Dataverse.operations.dataframe + +.. autoapi-nested-parse:: + + DataFrame CRUD operations namespace for the Dataverse SDK. + + + +Classes +------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.operations.dataframe.DataFrameOperations + + +Module Contents +--------------- + +.. py:class:: DataFrameOperations(client: PowerPlatform.Dataverse.client.DataverseClient) + + Namespace for pandas DataFrame CRUD operations. + + Accessed via ``client.dataframe``. Provides DataFrame-oriented wrappers + around the record-level CRUD operations. + + :param client: The parent :class:`~PowerPlatform.Dataverse.client.DataverseClient` instance. + :type client: ~PowerPlatform.Dataverse.client.DataverseClient + + Example:: + + import pandas as pd + + client = DataverseClient(base_url, credential) + + # Query records as a DataFrame + df = client.dataframe.get("account", select=["name"], top=100) + + # Create records from a DataFrame + new_df = pd.DataFrame([{"name": "Contoso"}, {"name": "Fabrikam"}]) + new_df["accountid"] = client.dataframe.create("account", new_df) + + # Update records + new_df["telephone1"] = ["555-0100", "555-0200"] + client.dataframe.update("account", new_df, id_column="accountid") + + # Delete records + client.dataframe.delete("account", new_df["accountid"]) + + + .. py:method:: sql(sql: str) -> pandas.DataFrame + + Execute a SQL query and return the results as a pandas DataFrame. + + Delegates to :meth:`~PowerPlatform.Dataverse.operations.query.QueryOperations.sql` + and converts the list of records into a single DataFrame. + + :param sql: Supported SQL SELECT statement. + :type sql: :class:`str` + + :return: DataFrame containing all result rows. Returns an empty + DataFrame when no rows match. + :rtype: ~pandas.DataFrame + + :raises ~PowerPlatform.Dataverse.core.errors.ValidationError: + If ``sql`` is not a string or is empty. + + .. rubric:: Example + + SQL query to DataFrame:: + + df = client.dataframe.sql( + "SELECT TOP 100 name, revenue FROM account " + "WHERE statecode = 0 ORDER BY revenue" + ) + print(f"Got {len(df)} rows") + print(df.head()) + + Aggregate query to DataFrame:: + + df = client.dataframe.sql( + "SELECT a.name, COUNT(c.contactid) as cnt " + "FROM account a " + "JOIN contact c ON a.accountid = c.parentcustomerid " + "GROUP BY a.name" + ) + + + + .. py:method:: get(table: str, record_id: Optional[str] = None, select: Optional[List[str]] = None, filter: Optional[str] = None, orderby: Optional[List[str]] = None, top: Optional[int] = None, expand: Optional[List[str]] = None, page_size: Optional[int] = None, count: bool = False, include_annotations: Optional[str] = None) -> pandas.DataFrame + + Fetch records and return as a single pandas DataFrame. + + When ``record_id`` is provided, returns a single-row DataFrame. + When ``record_id`` is None, internally iterates all pages and returns one + consolidated DataFrame. + + :param table: Schema name of the table (e.g. ``"account"`` or ``"new_MyTestTable"``). + :type table: :class:`str` + :param record_id: Optional GUID to fetch a specific record. If None, queries multiple records. + :type record_id: :class:`str` or None + :param select: Optional list of attribute logical names to retrieve. + :type select: list[str] or None + :param filter: Optional OData filter string. Column names must use exact lowercase logical names. + :type filter: :class:`str` or None + :param orderby: Optional list of attributes to sort by. + :type orderby: list[str] or None + :param top: Optional maximum number of records to return. + :type top: :class:`int` or None + :param expand: Optional list of navigation properties to expand (case-sensitive). + :type expand: list[str] or None + :param page_size: Optional number of records per page for pagination. + :type page_size: :class:`int` or None + :param count: If ``True``, adds ``$count=true`` to include a total + record count in the response. + :type count: :class:`bool` + :param include_annotations: OData annotation pattern for the + ``Prefer: odata.include-annotations`` header (e.g. ``"*"`` or + ``"OData.Community.Display.V1.FormattedValue"``), or ``None``. + :type include_annotations: :class:`str` or None + + :return: DataFrame containing all matching records. Returns an empty DataFrame + when no records match. + :rtype: ~pandas.DataFrame + + :raises ValueError: If ``record_id`` is not a non-empty string, or if + query parameters (``filter``, ``orderby``, ``top``, ``expand``, + ``page_size``) are provided alongside ``record_id``. + + .. tip:: + For large tables, use ``top`` or ``filter`` to limit the result set. + + .. rubric:: Example + + Fetch a single record as a DataFrame:: + + df = client.dataframe.get("account", record_id=account_id, select=["name", "telephone1"]) + print(df) + + Query with filtering:: + + df = client.dataframe.get("account", filter="statecode eq 0", select=["name"]) + print(f"Got {len(df)} active accounts") + + Limit result size:: + + df = client.dataframe.get("account", select=["name"], top=100) + + + + .. py:method:: create(table: str, records: pandas.DataFrame) -> pandas.Series + + Create records from a pandas DataFrame. + + :param table: Schema name of the table (e.g. ``"account"`` or ``"new_MyTestTable"``). + :type table: :class:`str` + :param records: DataFrame where each row is a record to create. + :type records: ~pandas.DataFrame + + :return: Series of created record GUIDs, aligned with the input DataFrame index. + :rtype: ~pandas.Series + + :raises TypeError: If ``records`` is not a pandas DataFrame. + :raises ValueError: If ``records`` is empty or the number of returned + IDs does not match the number of input rows. + + .. tip:: + All rows are sent in a single ``CreateMultiple`` request. For very + large DataFrames, consider splitting into smaller batches to avoid + request timeouts. + + .. rubric:: Example + + Create records from a DataFrame:: + + import pandas as pd + + df = pd.DataFrame([ + {"name": "Contoso", "telephone1": "555-0100"}, + {"name": "Fabrikam", "telephone1": "555-0200"}, + ]) + df["accountid"] = client.dataframe.create("account", df) + + + + .. py:method:: update(table: str, changes: pandas.DataFrame, id_column: str, clear_nulls: bool = False) -> None + + Update records from a pandas DataFrame. + + Each row in the DataFrame represents an update. The ``id_column`` specifies which + column contains the record GUIDs. + + :param table: Schema name of the table (e.g. ``"account"`` or ``"new_MyTestTable"``). + :type table: :class:`str` + :param changes: DataFrame where each row contains a record GUID and the fields to update. + :type changes: ~pandas.DataFrame + :param id_column: Name of the DataFrame column containing record GUIDs. + :type id_column: :class:`str` + :param clear_nulls: When ``False`` (default), missing values (NaN/None) are skipped + (the field is left unchanged on the server). When ``True``, missing values are sent + as ``null`` to Dataverse, clearing the field. Use ``True`` only when you intentionally + want NaN/None values to clear fields. + :type clear_nulls: :class:`bool` + + :raises TypeError: If ``changes`` is not a pandas DataFrame. + :raises ValueError: If ``changes`` is empty, ``id_column`` is not found in the + DataFrame, ``id_column`` contains invalid (non-string, empty, or whitespace-only) + values, or no updatable columns exist besides ``id_column``. + When ``clear_nulls`` is ``False`` (default), rows where all change values + are NaN/None produce empty patches and are silently skipped. If all rows + are skipped, the method returns without making an API call. When + ``clear_nulls`` is ``True``, NaN/None values become explicit nulls, so + rows are never skipped. + + .. tip:: + All rows are sent in a single ``UpdateMultiple`` request (or a + single PATCH for one row). For very large DataFrames, consider + splitting into smaller batches to avoid request timeouts. + + .. rubric:: Example + + Update records with different values per row:: + + import pandas as pd + + df = pd.DataFrame([ + {"accountid": "guid-1", "telephone1": "555-0100"}, + {"accountid": "guid-2", "telephone1": "555-0200"}, + ]) + client.dataframe.update("account", df, id_column="accountid") + + Broadcast the same change to all records:: + + df = pd.DataFrame({"accountid": ["guid-1", "guid-2", "guid-3"]}) + df["websiteurl"] = "https://example.com" + client.dataframe.update("account", df, id_column="accountid") + + Clear a field by setting clear_nulls=True:: + + df = pd.DataFrame([{"accountid": "guid-1", "websiteurl": None}]) + client.dataframe.update("account", df, id_column="accountid", clear_nulls=True) + + + + .. py:method:: delete(table: str, ids: pandas.Series, use_bulk_delete: bool = True) -> Optional[str] + + Delete records by passing a pandas Series of GUIDs. + + :param table: Schema name of the table (e.g. ``"account"`` or ``"new_MyTestTable"``). + :type table: :class:`str` + :param ids: Series of record GUIDs to delete. + :type ids: ~pandas.Series + :param use_bulk_delete: When ``True`` (default) and ``ids`` contains multiple values, execute the BulkDelete + action and return its async job identifier. When ``False`` each record is deleted sequentially. + :type use_bulk_delete: :class:`bool` + + :raises TypeError: If ``ids`` is not a pandas Series. + :raises ValueError: If ``ids`` contains invalid (non-string, empty, or + whitespace-only) values. + + :return: BulkDelete job ID when deleting multiple records via BulkDelete; + ``None`` when deleting a single record, using sequential deletion, or + when ``ids`` is empty. + :rtype: :class:`str` or None + + .. rubric:: Example + + Delete records using a Series:: + + import pandas as pd + + ids = pd.Series(["guid-1", "guid-2", "guid-3"]) + client.dataframe.delete("account", ids) + + + diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/files/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/files/index.rst.txt new file mode 100644 index 00000000..ad07ef55 --- /dev/null +++ b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/files/index.rst.txt @@ -0,0 +1,99 @@ +PowerPlatform.Dataverse.operations.files +======================================== + +.. py:module:: PowerPlatform.Dataverse.operations.files + +.. autoapi-nested-parse:: + + File operations namespace for the Dataverse SDK. + + + +Classes +------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.operations.files.FileOperations + + +Module Contents +--------------- + +.. py:class:: FileOperations(client: PowerPlatform.Dataverse.client.DataverseClient) + + Namespace for file operations. + + Accessed via ``client.files``. Provides file upload operations for + Dataverse file columns. + + :param client: The parent :class:`~PowerPlatform.Dataverse.client.DataverseClient` instance. + :type client: ~PowerPlatform.Dataverse.client.DataverseClient + + Example:: + + client = DataverseClient(base_url, credential) + + client.files.upload( + "account", account_id, "new_Document", "/path/to/file.pdf" + ) + + + .. py:method:: upload(table: str, record_id: str, file_column: str, path: str, *, mode: Optional[str] = None, mime_type: Optional[str] = None, if_none_match: bool = True) -> None + + Upload a file to a Dataverse file column. + + :param table: Schema name of the table (e.g. ``"account"`` or + ``"new_MyTestTable"``). + :type table: :class:`str` + :param record_id: GUID of the target record. + :type record_id: :class:`str` + :param file_column: Schema name of the file column attribute (e.g., + ``"new_Document"``). If the column doesn't exist, it will be + created automatically. + :type file_column: :class:`str` + :param path: Local filesystem path to the file. The stored filename + will be the basename of this path. + :type path: :class:`str` + :param mode: Upload strategy: ``"auto"`` (default), ``"small"``, or + ``"chunk"``. Auto mode selects small or chunked upload based on + file size. + :type mode: :class:`str` or None + :param mime_type: Explicit MIME type to store with the file (e.g. + ``"application/pdf"``). If not provided, defaults to + ``"application/octet-stream"``. + :type mime_type: :class:`str` or None + :param if_none_match: When True (default), sends + ``If-None-Match: null`` header to only succeed if the column is + currently empty. Set False to always overwrite using + ``If-Match: *``. + :type if_none_match: :class:`bool` + + :raises ~PowerPlatform.Dataverse.core.errors.HttpError: + If the upload fails or the file column is not empty when + ``if_none_match=True``. + :raises FileNotFoundError: If the specified file path does not exist. + + .. rubric:: Example + + Upload a PDF file:: + + client.files.upload( + "account", + account_id, + "new_Contract", + "/path/to/contract.pdf", + mime_type="application/pdf", + ) + + Upload with auto mode selection:: + + client.files.upload( + "email", + email_id, + "new_Attachment", + "/path/to/large_file.zip", + ) + + + diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/index.rst.txt new file mode 100644 index 00000000..cbb14381 --- /dev/null +++ b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/index.rst.txt @@ -0,0 +1,28 @@ +PowerPlatform.Dataverse.operations +================================== + +.. py:module:: PowerPlatform.Dataverse.operations + +.. autoapi-nested-parse:: + + Operation namespaces for the Dataverse SDK. + + This module contains the operation namespace classes that organize + SDK operations into logical groups: records, query, and tables. + + + +Submodules +---------- + +.. toctree:: + :maxdepth: 1 + + /autoapi/PowerPlatform/Dataverse/operations/batch/index + /autoapi/PowerPlatform/Dataverse/operations/dataframe/index + /autoapi/PowerPlatform/Dataverse/operations/files/index + /autoapi/PowerPlatform/Dataverse/operations/query/index + /autoapi/PowerPlatform/Dataverse/operations/records/index + /autoapi/PowerPlatform/Dataverse/operations/tables/index + + diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/query/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/query/index.rst.txt new file mode 100644 index 00000000..740ab5b4 --- /dev/null +++ b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/query/index.rst.txt @@ -0,0 +1,345 @@ +PowerPlatform.Dataverse.operations.query +======================================== + +.. py:module:: PowerPlatform.Dataverse.operations.query + +.. autoapi-nested-parse:: + + Query operations namespace for the Dataverse SDK. + + + +Classes +------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.operations.query.QueryOperations + + +Module Contents +--------------- + +.. py:class:: QueryOperations(client: PowerPlatform.Dataverse.client.DataverseClient) + + Namespace for query operations. + + Accessed via ``client.query``. Provides query and search operations + against Dataverse tables. + + :param client: The parent :class:`~PowerPlatform.Dataverse.client.DataverseClient` instance. + :type client: ~PowerPlatform.Dataverse.client.DataverseClient + + Example:: + + from PowerPlatform.Dataverse.models.filters import col + + client = DataverseClient(base_url, credential) + + # Fluent query builder (recommended) + for record in (client.query.builder("account") + .select("name", "revenue") + .where(col("statecode") == 0) + .order_by("revenue", descending=True) + .top(100) + .execute()): + print(record["name"]) + + # SQL query + rows = client.query.sql("SELECT TOP 10 name FROM account ORDER BY name") + for row in rows: + print(row["name"]) + + + .. py:method:: builder(table: str) -> PowerPlatform.Dataverse.models.query_builder.QueryBuilder + + Create a fluent query builder for the specified table. + + Returns a :class:`~PowerPlatform.Dataverse.models.query_builder.QueryBuilder` + that can be chained with filter, select, and order methods, then + executed directly via ``.execute()``. + + :param table: Table schema name (e.g. ``"account"``). + :type table: :class:`str` + :return: A QueryBuilder instance bound to this client. + :rtype: ~PowerPlatform.Dataverse.models.query_builder.QueryBuilder + + .. rubric:: Example + + Build and execute a query fluently:: + + from PowerPlatform.Dataverse.models.filters import col + + for record in (client.query.builder("account") + .select("name", "revenue") + .where(col("statecode") == 0) + .where(col("revenue") > 1_000_000) + .order_by("revenue", descending=True) + .top(100) + .page_size(50) + .execute()): + print(record["name"]) + + With composable expression tree:: + + from PowerPlatform.Dataverse.models.filters import col + + for record in (client.query.builder("account") + .where((col("statecode") == 0) | (col("statecode") == 1)) + .where(col("revenue") > 100_000) + .execute()): + print(record["name"]) + + + + .. py:method:: sql(sql: str) -> List[PowerPlatform.Dataverse.models.record.Record] + + Execute a read-only SQL query using the Dataverse Web API. + + The Dataverse SQL endpoint supports a broad subset of T-SQL:: + + SELECT / SELECT DISTINCT / SELECT TOP N (0-5000) + FROM table [alias] + INNER JOIN / LEFT JOIN (multi-table, no depth limit) + WHERE (=, !=, >, <, >=, <=, LIKE, IN, NOT IN, IS NULL, + IS NOT NULL, BETWEEN, AND, OR, nested parentheses) + GROUP BY column + ORDER BY column [ASC|DESC] + OFFSET n ROWS FETCH NEXT m ROWS ONLY + COUNT(*), SUM(), AVG(), MIN(), MAX() + + ``SELECT *`` is not supported -- specify column names explicitly. + Use :meth:`sql_columns` to discover available column names for a table. + + Not supported: SELECT *, subqueries, CTE, HAVING, UNION, + RIGHT/FULL/CROSS JOIN, CASE, COALESCE, window functions, + string/date/math functions, INSERT/UPDATE/DELETE. For writes, use + ``client.records`` methods. + + :param sql: Supported SQL SELECT statement. + :type sql: :class:`str` + + :return: List of :class:`~PowerPlatform.Dataverse.models.record.Record` + objects. Returns an empty list when no rows match. + :rtype: list[~PowerPlatform.Dataverse.models.record.Record] + + :raises ~PowerPlatform.Dataverse.core.errors.ValidationError: + If ``sql`` is not a string or is empty. + + .. rubric:: Example + + Basic query:: + + rows = client.query.sql( + "SELECT TOP 10 name FROM account ORDER BY name" + ) + + JOIN with aggregation:: + + rows = client.query.sql( + "SELECT a.name, COUNT(c.contactid) as cnt " + "FROM account a " + "JOIN contact c ON a.accountid = c.parentcustomerid " + "GROUP BY a.name" + ) + + + + .. py:method:: fetchxml(xml: str) -> PowerPlatform.Dataverse.models.fetchxml_query.FetchXmlQuery + + Return an inert :class:`~PowerPlatform.Dataverse.models.fetchxml_query.FetchXmlQuery` object. + + No HTTP request is made until + :meth:`~PowerPlatform.Dataverse.models.fetchxml_query.FetchXmlQuery.execute` + or + :meth:`~PowerPlatform.Dataverse.models.fetchxml_query.FetchXmlQuery.execute_pages` + is called on the returned object. + + Use for SQL-JOIN scenarios, aggregate queries, or other operations that + the OData builder endpoint cannot express. + + :param xml: Well-formed FetchXML query string. The root ```` + element determines the entity set endpoint. + :type xml: :class:`str` + :return: Inert query object with ``.execute()`` and ``.execute_pages()`` methods. + :rtype: :class:`~PowerPlatform.Dataverse.models.fetchxml_query.FetchXmlQuery` + :raises ValueError: If the FetchXML is missing a root ```` element + or the entity ``name`` attribute. + + Example:: + + query = client.query.fetchxml(""" + + + + + + + + + """) + + # Eager — collect all pages: + result = query.execute() + df = result.to_dataframe() + + # Lazy — process one page at a time: + for page in query.execute_pages(): + process(page.to_dataframe()) + + + + .. py:method:: sql_columns(table: str, *, include_system: bool = False) -> List[Dict[str, Any]] + + Return a simplified list of SQL-usable columns for a table. + + Each dict contains ``name`` (logical name for SQL), ``type`` + (Dataverse attribute type), ``is_pk`` (primary key flag), and + ``label`` (display name). Virtual columns are always excluded + because the SQL endpoint cannot query them. + + :param table: Schema name of the table (e.g. ``"account"``). + :type table: :class:`str` + :param include_system: When ``False`` (default), columns that end + with common system suffixes (``_base``, ``versionnumber``, + ``timezoneruleversionnumber``, ``utcconversiontimezonecode``, + ``importsequencenumber``, ``overriddencreatedon``) are excluded. + :type include_system: :class:`bool` + + :return: List of column metadata dicts. + :rtype: list[dict[str, typing.Any]] + + Example:: + + cols = client.query.sql_columns("account") + for c in cols: + print(f"{c['name']:30s} {c['type']:20s} PK={c['is_pk']}") + + + + .. py:method:: odata_select(table: str, *, include_system: bool = False) -> List[str] + + Return a list of column logical names suitable for ``$select``. + + Can be passed directly to ``client.records.get(table, select=...)``. + + :param table: Schema name of the table (e.g. ``"account"``). + :type table: :class:`str` + :param include_system: Include system columns (default ``False``). + :type include_system: :class:`bool` + + :return: List of lowercase column logical names. + :rtype: list[str] + + Example:: + + cols = client.query.odata_select("account") + for page in client.records.get("account", select=cols, top=10): + for r in page: + print(r) + + + + .. py:method:: odata_expands(table: str) -> List[Dict[str, Any]] + + Discover all ``$expand`` navigation properties from a table. + + Returns entries for each outgoing lookup (single-valued navigation + property). Each entry contains the exact PascalCase navigation + property name needed for ``$expand`` and ``@odata.bind``, plus + the target entity set name. + + :param table: Schema name of the table (e.g. ``"contact"``). + :type table: :class:`str` + + :return: List of dicts, each with: + + - ``nav_property`` -- PascalCase navigation property for $expand + - ``target_table`` -- target entity logical name + - ``target_entity_set`` -- target entity set (for @odata.bind) + - ``lookup_attribute`` -- the lookup column logical name + - ``relationship`` -- relationship schema name + + :rtype: list[dict[str, typing.Any]] + + Example:: + + expands = client.query.odata_expands("contact") + for e in expands: + print(f"expand={e['nav_property']} -> {e['target_table']}") + + # Use in a query + e = next(e for e in expands if e['target_table'] == 'account') + for page in client.records.get("contact", + select=["fullname"], + expand=[e['nav_property']]): + ... + + + + .. py:method:: odata_expand(from_table: str, to_table: str) -> str + + Return the navigation property name to ``$expand`` from one table to another. + + Discovers via relationship metadata. Returns the exact PascalCase + string for the ``expand=`` parameter. + + :param from_table: Schema name of the source table (e.g. ``"contact"``). + :type from_table: :class:`str` + :param to_table: Schema name of the target table (e.g. ``"account"``). + :type to_table: :class:`str` + + :return: The navigation property name (PascalCase). + :rtype: :class:`str` + + :raises ValueError: If no navigation property found for the target. + + Example:: + + nav = client.query.odata_expand("contact", "account") + # Returns e.g. "parentcustomerid_account" + for page in client.records.get("contact", + select=["fullname"], + expand=[nav], + top=5): + for r in page: + acct = r.get(nav) or {} + print(f"{r['fullname']} -> {acct.get('name', 'N/A')}") + + + + .. py:method:: odata_bind(from_table: str, to_table: str, target_id: str) -> Dict[str, str] + + Build an ``@odata.bind`` entry for setting a lookup field. + + Auto-discovers the navigation property name and entity set name + from metadata. Returns a single-entry dict that can be merged + into a create or update payload. + + :param from_table: Schema name of the entity being created/updated. + :type from_table: :class:`str` + :param to_table: Schema name of the target entity the lookup points to. + :type to_table: :class:`str` + :param target_id: GUID of the target record. + :type target_id: :class:`str` + + :return: A dict like ``{"NavProp@odata.bind": "/entityset(guid)"}``. + :rtype: dict[str, str] + + :raises ValueError: If no relationship found between the tables. + + Example:: + + # Instead of manually constructing: + # {"parentcustomerid_account@odata.bind": "/accounts(guid)"} + # Just do: + bind = client.query.odata_bind("contact", "account", acct_id) + client.records.create("contact", { + "firstname": "Jane", + "lastname": "Doe", + **bind, + }) + + + diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/records/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/records/index.rst.txt new file mode 100644 index 00000000..b76711dd --- /dev/null +++ b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/records/index.rst.txt @@ -0,0 +1,461 @@ +PowerPlatform.Dataverse.operations.records +========================================== + +.. py:module:: PowerPlatform.Dataverse.operations.records + +.. autoapi-nested-parse:: + + Record CRUD operations namespace for the Dataverse SDK. + + + +Classes +------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.operations.records.RecordOperations + + +Module Contents +--------------- + +.. py:class:: RecordOperations(client: PowerPlatform.Dataverse.client.DataverseClient) + + Namespace for record-level CRUD operations. + + Accessed via ``client.records``. Provides create, update, delete, and get + operations on individual Dataverse records. + + :param client: The parent :class:`~PowerPlatform.Dataverse.client.DataverseClient` instance. + :type client: ~PowerPlatform.Dataverse.client.DataverseClient + + Example:: + + client = DataverseClient(base_url, credential) + + # Create a single record + guid = client.records.create("account", {"name": "Contoso Ltd"}) + + # Get a record + record = client.records.get("account", guid, select=["name"]) + + # Update a record + client.records.update("account", guid, {"telephone1": "555-0100"}) + + # Delete a record + client.records.delete("account", guid) + + + .. py:method:: create(table: str, data: Dict[str, Any]) -> str + create(table: str, data: List[Dict[str, Any]]) -> List[str] + + Create one or more records in a Dataverse table. + + When ``data`` is a single dictionary, creates one record and returns its + GUID as a string. When ``data`` is a list of dictionaries, creates all + records via the ``CreateMultiple`` action and returns a list of GUIDs. + + :param table: Schema name of the table (e.g. ``"account"`` or ``"new_MyTestTable"``). + :type table: :class:`str` + :param data: A single record dictionary or a list of record dictionaries. + Each dictionary maps column schema names to values. + :type data: dict or list[dict] + + :return: A single GUID string for a single record, or a list of GUID + strings for bulk creation. + :rtype: str or list[str] + + :raises TypeError: If ``data`` is not a dict or list[dict]. + + .. rubric:: Example + + Create a single record:: + + guid = client.records.create("account", {"name": "Contoso"}) + print(f"Created: {guid}") + + Create multiple records:: + + guids = client.records.create("account", [ + {"name": "Contoso"}, + {"name": "Fabrikam"}, + ]) + print(f"Created {len(guids)} accounts") + + + + .. py:method:: update(table: str, ids: Union[str, List[str]], changes: Union[Dict[str, Any], List[Dict[str, Any]]]) -> None + + Update one or more records in a Dataverse table. + + Supports three usage patterns: + + 1. **Single** -- ``update("account", "guid", {"name": "New"})`` + 2. **Broadcast** -- ``update("account", [id1, id2], {"status": 1})`` + applies the same changes dict to every ID. + 3. **Paired** -- ``update("account", [id1, id2], [ch1, ch2])`` + applies each changes dict to its corresponding ID (lists must be + equal length). + + :param table: Schema name of the table (e.g. ``"account"``). + :type table: :class:`str` + :param ids: A single GUID string, or a list of GUID strings. + :type ids: str or list[str] + :param changes: A dictionary of field changes (single/broadcast), or a + list of dictionaries (paired, one per ID). + :type changes: dict or list[dict] + + :raises TypeError: If ``ids`` is not str or list[str], or if ``changes`` + does not match the expected pattern. + + .. rubric:: Example + + Single update:: + + client.records.update("account", account_id, {"telephone1": "555-0100"}) + + Broadcast update:: + + client.records.update("account", [id1, id2], {"statecode": 1}) + + Paired update:: + + client.records.update( + "account", + [id1, id2], + [{"name": "Name A"}, {"name": "Name B"}], + ) + + + + .. py:method:: delete(table: str, ids: str) -> None + delete(table: str, ids: List[str], *, use_bulk_delete: bool = True) -> Optional[str] + + Delete one or more records from a Dataverse table. + + When ``ids`` is a single string, deletes that one record. When ``ids`` + is a list, either executes a BulkDelete action (returning the async job + ID) or deletes each record sequentially depending on ``use_bulk_delete``. + + :param table: Schema name of the table (e.g. ``"account"``). + :type table: :class:`str` + :param ids: A single GUID string, or a list of GUID strings. + :type ids: str or list[str] + :param use_bulk_delete: When True (default) and ``ids`` is a list, use + the BulkDelete action and return its async job ID. When False, delete + records one at a time. + :type use_bulk_delete: :class:`bool` + + :return: The BulkDelete job ID when bulk-deleting; otherwise None. + :rtype: :class:`str` or None + + :raises TypeError: If ``ids`` is not str or list[str]. + + .. rubric:: Example + + Delete a single record:: + + client.records.delete("account", account_id) + + Bulk delete:: + + job_id = client.records.delete("account", [id1, id2, id3]) + + + + .. py:method:: get(table: str, record_id: str, *, select: Optional[List[str]] = None) -> PowerPlatform.Dataverse.models.record.Record + get(table: str, *, select: Optional[List[str]] = None, filter: Optional[str] = None, orderby: Optional[List[str]] = None, top: Optional[int] = None, expand: Optional[List[str]] = None, page_size: Optional[int] = None, count: bool = False, include_annotations: Optional[str] = None) -> Iterable[List[PowerPlatform.Dataverse.models.record.Record]] + + Fetch a single record by ID, or fetch multiple records with pagination. + + This method has two usage patterns: + + **Fetch a single record** -- ``get(table, record_id, *, select=...)`` + + Pass ``record_id`` as a positional argument to retrieve one record + and get back a :class:`dict`. Query parameters (``filter``, + ``orderby``, ``top``, ``expand``, ``page_size``) must not be provided. + + **Fetch multiple records** -- ``get(table, *, select=..., filter=..., ...)`` + + Omit ``record_id`` to perform a paginated fetch and get back a + generator that yields one page (list of record dicts) at a time. + Automatically follows ``@odata.nextLink`` for server-side paging. + + :param table: Schema name of the table (e.g. ``"account"`` or + ``"new_MyTestTable"``). + :type table: :class:`str` + :param record_id: GUID of the record to retrieve. When omitted, + performs a multi-record fetch instead. + :type record_id: :class:`str` or None + :param select: Optional list of column logical names to include. + Column names are automatically lowercased. + :type select: list[str] or None + :param filter: Optional OData ``$filter`` expression (e.g. + ``"name eq 'Contoso'"``). Column names in filter expressions must + use exact lowercase logical names. Only used for multi-record + queries. + :type filter: :class:`str` or None + :param orderby: Optional list of sort expressions (e.g. + ``["name asc", "createdon desc"]``). Column names are + automatically lowercased. Only used for multi-record queries. + :type orderby: list[str] or None + :param top: Optional maximum total number of records to return. Only + used for multi-record queries. + :type top: :class:`int` or None + :param expand: Optional list of navigation properties to expand (e.g. + ``["primarycontactid"]``). Case-sensitive; must match + server-defined names exactly. Only used for multi-record queries. + :type expand: list[str] or None + :param page_size: Optional per-page size hint sent via + ``Prefer: odata.maxpagesize``. Only used for multi-record queries. + :type page_size: :class:`int` or None + :param count: If ``True``, adds ``$count=true`` to include a total + record count in the response. Only used for multi-record queries. + :type count: :class:`bool` + :param include_annotations: OData annotation pattern for the + ``Prefer: odata.include-annotations`` header (e.g. ``"*"`` or + ``"OData.Community.Display.V1.FormattedValue"``), or ``None``. + Only used for multi-record queries. + :type include_annotations: :class:`str` or None + + :return: A single :class:`~PowerPlatform.Dataverse.models.record.Record` + when ``record_id`` is provided, or a generator yielding pages + (lists of :class:`~PowerPlatform.Dataverse.models.record.Record`) + when fetching multiple records. + :rtype: ~PowerPlatform.Dataverse.models.record.Record or + collections.abc.Iterable[list[~PowerPlatform.Dataverse.models.record.Record]] + + :raises TypeError: If ``record_id`` is provided but not a string. + :raises ValueError: If query parameters are provided alongside + ``record_id``. + + .. rubric:: Example + + Fetch a single record:: + + record = client.records.get( + "account", account_id, select=["name", "telephone1"] + ) + print(record["name"]) + + Fetch multiple records with pagination:: + + for page in client.records.get( + "account", + filter="statecode eq 0", + select=["name", "telephone1"], + page_size=50, + ): + for record in page: + print(record["name"]) + + + + .. py:method:: retrieve(table: str, record_id: str, *, select: Optional[List[str]] = None, expand: Optional[List[str]] = None, include_annotations: Optional[str] = None) -> Optional[PowerPlatform.Dataverse.models.record.Record] + + Fetch a single record by its GUID, returning ``None`` if not found. + + GA replacement for ``records.get(table, record_id)``. Returns ``None`` + instead of raising when the record does not exist (HTTP 404). + + :param table: Schema name of the table (e.g. ``"account"``). + :type table: :class:`str` + :param record_id: GUID of the record to retrieve. + :type record_id: :class:`str` + :param select: Optional list of column logical names to include. + :type select: list[str] or None + :param expand: Optional list of navigation properties to expand (e.g. + ``["primarycontactid"]``). Navigation property names are + case-sensitive and must match the entity's ``$metadata``. + :type expand: list[str] or None + :param include_annotations: OData annotation pattern for the + ``Prefer: odata.include-annotations`` header (e.g. ``"*"`` or + ``"OData.Community.Display.V1.FormattedValue"``), or ``None``. + :type include_annotations: :class:`str` or None + :return: Typed record, or ``None`` if not found. + :rtype: :class:`~PowerPlatform.Dataverse.models.record.Record` or None + + Example:: + + record = client.records.retrieve( + "account", account_id, + select=["name", "statuscode"], + expand=["primarycontactid"], + include_annotations="OData.Community.Display.V1.FormattedValue", + ) + if record is not None: + contact = record.get("primarycontactid") or {} + print(contact.get("fullname")) + + + + .. py:method:: list(table: str, *, filter: Optional[Union[str, PowerPlatform.Dataverse.models.filters.FilterExpression]] = None, select: Optional[List[str]] = None, orderby: Optional[List[str]] = None, top: Optional[int] = None, expand: Optional[List[str]] = None, page_size: Optional[int] = None, count: bool = False, include_annotations: Optional[str] = None) -> PowerPlatform.Dataverse.models.record.QueryResult + + Fetch multiple records and return them as a :class:`~PowerPlatform.Dataverse.models.record.QueryResult`. + + GA replacement for ``records.get(table, filter=...)``. All pages are + collected eagerly and returned as a single :class:`~PowerPlatform.Dataverse.models.record.QueryResult`. + + :param table: Schema name of the table (e.g. ``"account"``). + :type table: :class:`str` + :param filter: Optional OData filter string or :class:`~PowerPlatform.Dataverse.models.filters.FilterExpression`. + :type filter: str or FilterExpression or None + :param select: Optional list of column logical names to include. + :type select: list[str] or None + :param orderby: Optional list of sort expressions (e.g. ``["name asc", "createdon desc"]``). + :type orderby: list[str] or None + :param top: Maximum total number of records to return. + :type top: int or None + :param expand: Optional list of navigation properties to expand. + :type expand: list[str] or None + :param page_size: Per-page size hint via ``Prefer: odata.maxpagesize``. + :type page_size: int or None + :param count: If ``True``, adds ``$count=true`` to include a total record count. + :type count: bool + :param include_annotations: OData annotation pattern for the + ``Prefer: odata.include-annotations`` header, or ``None``. + :type include_annotations: :class:`str` or None + :return: All matching records collected into a :class:`~PowerPlatform.Dataverse.models.record.QueryResult`. + :rtype: :class:`~PowerPlatform.Dataverse.models.record.QueryResult` + + Example:: + + from PowerPlatform.Dataverse import col + + result = client.records.list( + "account", + filter=col("statecode") == 0, + select=["name", "statuscode"], + orderby=["name asc"], + top=100, + include_annotations="OData.Community.Display.V1.FormattedValue", + ) + for record in result: + print(record["name"], record.get("statuscode@OData.Community.Display.V1.FormattedValue")) + + + + .. py:method:: list_pages(table: str, *, filter: Optional[Union[str, PowerPlatform.Dataverse.models.filters.FilterExpression]] = None, select: Optional[List[str]] = None, orderby: Optional[List[str]] = None, top: Optional[int] = None, expand: Optional[List[str]] = None, page_size: Optional[int] = None, count: bool = False, include_annotations: Optional[str] = None) -> Iterator[PowerPlatform.Dataverse.models.record.QueryResult] + + Lazily yield one :class:`~PowerPlatform.Dataverse.models.record.QueryResult` per HTTP page. + + Streaming counterpart to :meth:`list`. Each iteration triggers one + network request via ``@odata.nextLink``. One-shot — do not iterate + more than once. + + :param table: Schema name of the table (e.g. ``"account"``). + :type table: :class:`str` + :param filter: Optional OData filter string or :class:`~PowerPlatform.Dataverse.models.filters.FilterExpression`. + :type filter: str or FilterExpression or None + :param select: Optional list of column logical names to include. + :type select: list[str] or None + :param orderby: Optional list of sort expressions (e.g. ``["name asc", "createdon desc"]``). + :type orderby: list[str] or None + :param top: Maximum total number of records to return. + :type top: int or None + :param expand: Optional list of navigation properties to expand. + :type expand: list[str] or None + :param page_size: Per-page size hint via ``Prefer: odata.maxpagesize``. + :type page_size: int or None + :param count: If ``True``, adds ``$count=true`` to include a total record count. + :type count: bool + :param include_annotations: OData annotation pattern for the + ``Prefer: odata.include-annotations`` header, or ``None``. + :type include_annotations: :class:`str` or None + :return: Iterator of per-page :class:`~PowerPlatform.Dataverse.models.record.QueryResult` objects. + :rtype: Iterator[:class:`~PowerPlatform.Dataverse.models.record.QueryResult`] + + Example:: + + for page in client.records.list_pages( + "account", + filter="statecode eq 0", + orderby=["name asc"], + page_size=200, + ): + process(page.to_dataframe()) + + + + .. py:method:: upsert(table: str, items: List[Union[PowerPlatform.Dataverse.models.upsert.UpsertItem, Dict[str, Any]]]) -> None + + Upsert one or more records identified by alternate keys. + + When ``items`` contains a single entry, performs a single upsert via PATCH + using the alternate key in the URL. When ``items`` contains multiple entries, + uses the ``UpsertMultiple`` bulk action. + + Each item must be either a :class:`~PowerPlatform.Dataverse.models.upsert.UpsertItem` + or a plain ``dict`` with ``"alternate_key"`` and ``"record"`` keys (both dicts). + + :param table: Schema name of the table (e.g. ``"account"`` or ``"new_MyTestTable"``). + :type table: str + :param items: Non-empty list of :class:`~PowerPlatform.Dataverse.models.upsert.UpsertItem` + instances or dicts with ``"alternate_key"`` and ``"record"`` keys. + :type items: list[UpsertItem | dict] + + :return: ``None`` + :rtype: None + + :raises TypeError: If ``items`` is not a non-empty list, or if any element is + neither a :class:`~PowerPlatform.Dataverse.models.upsert.UpsertItem` nor a + dict with ``"alternate_key"`` and ``"record"`` keys. + + .. rubric:: Example + + Upsert a single record using ``UpsertItem``:: + + from PowerPlatform.Dataverse.models import UpsertItem + + client.records.upsert("account", [ + UpsertItem( + alternate_key={"accountnumber": "ACC-001"}, + record={"name": "Contoso Ltd", "description": "Primary account"}, + ) + ]) + + Upsert a single record using a plain dict:: + + client.records.upsert("account", [ + { + "alternate_key": {"accountnumber": "ACC-001"}, + "record": {"name": "Contoso Ltd", "description": "Primary account"}, + }, + ]) + + Upsert multiple records using ``UpsertItem``:: + + from PowerPlatform.Dataverse.models import UpsertItem + + client.records.upsert("account", [ + UpsertItem( + alternate_key={"accountnumber": "ACC-001"}, + record={"name": "Contoso Ltd", "description": "Primary account"}, + ), + UpsertItem( + alternate_key={"accountnumber": "ACC-002"}, + record={"name": "Fabrikam Inc", "description": "Partner account"}, + ), + ]) + + Upsert multiple records using plain dicts:: + + client.records.upsert("account", [ + { + "alternate_key": {"accountnumber": "ACC-001"}, + "record": {"name": "Contoso Ltd", "description": "Primary account"}, + }, + { + "alternate_key": {"accountnumber": "ACC-002"}, + "record": {"name": "Fabrikam Inc", "description": "Partner account"}, + }, + ]) + + The ``alternate_key`` dict may contain multiple columns when the configured + alternate key is composite, e.g. + ``{"accountnumber": "ACC-001", "address1_postalcode": "98052"}``. + + + diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/tables/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/tables/index.rst.txt new file mode 100644 index 00000000..0cf63a2e --- /dev/null +++ b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/tables/index.rst.txt @@ -0,0 +1,683 @@ +PowerPlatform.Dataverse.operations.tables +========================================= + +.. py:module:: PowerPlatform.Dataverse.operations.tables + +.. autoapi-nested-parse:: + + Table metadata operations namespace for the Dataverse SDK. + + + +Classes +------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.operations.tables.TableOperations + + +Module Contents +--------------- + +.. py:class:: TableOperations(client: PowerPlatform.Dataverse.client.DataverseClient) + + Namespace for table-level metadata operations. + + Accessed via ``client.tables``. Provides operations to create, delete, + inspect, and list Dataverse tables, as well as add and remove columns. + + :param client: The parent :class:`~PowerPlatform.Dataverse.client.DataverseClient` instance. + :type client: ~PowerPlatform.Dataverse.client.DataverseClient + + Example:: + + client = DataverseClient(base_url, credential) + + # Create a table + info = client.tables.create( + "new_Product", + {"new_Price": "decimal", "new_InStock": "bool"}, + solution="MySolution", + ) + + # List tables + tables = client.tables.list() + + # Get table info + info = client.tables.get("new_Product") + + # Add columns + client.tables.add_columns("new_Product", {"new_Rating": "int"}) + + # Remove columns + client.tables.remove_columns("new_Product", "new_Rating") + + # Delete a table + client.tables.delete("new_Product") + + + .. py:method:: create(table: str, columns: Dict[str, Any], *, solution: Optional[str] = None, primary_column: Optional[str] = None, display_name: Optional[str] = None) -> PowerPlatform.Dataverse.models.table_info.TableInfo + + Create a custom table with the specified columns. + + :param table: Schema name of the table with customization prefix + (e.g. ``"new_MyTestTable"``). + :type table: :class:`str` + :param columns: Mapping of column schema names (with customization + prefix) to their types. Supported types include ``"string"`` + (or ``"text"``), ``"memo"`` (or ``"multiline"``), + ``"int"`` (or ``"integer"``), ``"decimal"`` + (or ``"money"``), ``"float"`` (or ``"double"``), ``"datetime"`` + (or ``"date"``), ``"bool"`` (or ``"boolean"``), ``"file"``, and + ``Enum`` subclasses + (for local option sets). + :type columns: :class:`dict` + :param solution: Optional solution unique name that should own the new + table. When omitted the table is created in the default solution. + :type solution: :class:`str` or None + :param primary_column: Optional primary name column schema name with + customization prefix (e.g. ``"new_ProductName"``). If not provided, + defaults to ``"{prefix}_Name"``. + :type primary_column: :class:`str` or None + :param display_name: Human-readable display name for the table + (e.g. ``"Product"``). When omitted, defaults to the table schema name. + :type display_name: :class:`str` or None + + :return: Table metadata with ``schema_name``, ``entity_set_name``, + ``logical_name``, ``metadata_id``, and ``columns_created``. + Supports dict-like access with legacy keys for backward + compatibility. + :rtype: :class:`~PowerPlatform.Dataverse.models.table_info.TableInfo` + + :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: + If table creation fails or the table already exists. + + .. rubric:: Example + + Create a table with simple columns:: + + from enum import IntEnum + + class ItemStatus(IntEnum): + ACTIVE = 1 + INACTIVE = 2 + + result = client.tables.create( + "new_Product", + { + "new_Title": "string", + "new_Price": "decimal", + "new_Status": ItemStatus, + }, + solution="MySolution", + primary_column="new_ProductName", + display_name="Product", + ) + print(f"Created: {result['table_schema_name']}") + + + + .. py:method:: delete(table: str) -> None + + Delete a custom table by schema name. + + :param table: Schema name of the table (e.g. ``"new_MyTestTable"``). + :type table: :class:`str` + + :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: + If the table does not exist or deletion fails. + + .. warning:: + This operation is irreversible and will delete all records in the + table along with the table definition. + + Example:: + + client.tables.delete("new_MyTestTable") + + + + .. py:method:: get(table: str) -> Optional[PowerPlatform.Dataverse.models.table_info.TableInfo] + + Get basic metadata for a table if it exists. + + :param table: Schema name of the table (e.g. ``"new_MyTestTable"`` + or ``"account"``). + :type table: :class:`str` + + :return: Table metadata, or ``None`` if the table is not found. + Supports dict-like access with legacy keys for backward + compatibility. + :rtype: :class:`~PowerPlatform.Dataverse.models.table_info.TableInfo` + or None + + Example:: + + info = client.tables.get("new_MyTestTable") + if info: + print(f"Logical name: {info['table_logical_name']}") + print(f"Entity set: {info['entity_set_name']}") + + + + .. py:method:: list(*, filter: Optional[str] = None, select: Optional[List[str]] = None) -> List[Dict[str, Any]] + + List all non-private tables in the Dataverse environment. + + By default returns every table where ``IsPrivate eq false``. Supply + an optional OData ``$filter`` expression to further narrow the results. + The expression is combined with the default ``IsPrivate eq false`` + clause using ``and``. + + :param filter: Optional OData ``$filter`` expression to further narrow + the list of returned tables (e.g. + ``"SchemaName eq 'Account'"``). Column names in filter + expressions must use the exact property names from the + ``EntityDefinitions`` metadata (typically PascalCase). + :type filter: :class:`str` or None + :param select: Optional list of property names to include in the + response (projected via the OData ``$select`` query option). + Property names must use the exact PascalCase names from the + ``EntityDefinitions`` metadata (e.g. + ``["LogicalName", "SchemaName", "DisplayName"]``). + When ``None`` (the default) or an empty list, all properties are + returned. + :type select: list[str] or None + + :return: List of EntityDefinition metadata dictionaries. + :rtype: list[dict] + + Example:: + + # List all non-private tables + tables = client.tables.list() + for table in tables: + print(table["LogicalName"]) + + # List only tables whose schema name starts with "new_" + custom_tables = client.tables.list( + filter="startswith(SchemaName, 'new_')" + ) + + # List tables with only specific properties + tables = client.tables.list( + select=["LogicalName", "SchemaName", "EntitySetName"] + ) + + + + .. py:method:: add_columns(table: str, columns: Dict[str, Any]) -> List[str] + + Add one or more columns to an existing table. + + :param table: Schema name of the table (e.g. ``"new_MyTestTable"``). + :type table: :class:`str` + :param columns: Mapping of column schema names (with customization + prefix) to their types. Supported types are the same as for + :meth:`create`. + :type columns: :class:`dict` + + :return: Schema names of the columns that were created. + :rtype: list[str] + + :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: + If the table does not exist. + + Example:: + + created = client.tables.add_columns( + "new_MyTestTable", + {"new_Notes": "string", "new_Active": "bool"}, + ) + print(created) # ['new_Notes', 'new_Active'] + + + + .. py:method:: remove_columns(table: str, columns: Union[str, List[str]]) -> List[str] + + Remove one or more columns from a table. + + :param table: Schema name of the table (e.g. ``"new_MyTestTable"``). + :type table: :class:`str` + :param columns: Column schema name or list of column schema names to + remove. Must include the customization prefix (e.g. + ``"new_TestColumn"``). + :type columns: str or list[str] + + :return: Schema names of the columns that were removed. + :rtype: list[str] + + :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: + If the table or a specified column does not exist. + + Example:: + + removed = client.tables.remove_columns( + "new_MyTestTable", + ["new_Notes", "new_Active"], + ) + print(removed) # ['new_Notes', 'new_Active'] + + + + .. py:method:: create_one_to_many_relationship(lookup: PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata, relationship: PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata, *, solution: Optional[str] = None) -> PowerPlatform.Dataverse.models.relationship.RelationshipInfo + + Create a one-to-many relationship between tables. + + This operation creates both the relationship and the lookup attribute + on the referencing table. + + :param lookup: Metadata defining the lookup attribute. + :type lookup: ~PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata + :param relationship: Metadata defining the relationship. + :type relationship: ~PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata + :param solution: Optional solution unique name to add relationship to. + :type solution: :class:`str` or None + + :return: Relationship metadata with ``relationship_id``, + ``relationship_schema_name``, ``relationship_type``, + ``lookup_schema_name``, ``referenced_entity``, and + ``referencing_entity``. + :rtype: :class:`~PowerPlatform.Dataverse.models.relationship.RelationshipInfo` + + :raises ~PowerPlatform.Dataverse.core.errors.HttpError: + If the Web API request fails. + + .. rubric:: Example + + Create a one-to-many relationship: Department (1) -> Employee (N):: + + from PowerPlatform.Dataverse.models import ( + LookupAttributeMetadata, + OneToManyRelationshipMetadata, + Label, + LocalizedLabel, + CascadeConfiguration, + ) + from PowerPlatform.Dataverse.common.constants import ( + CASCADE_BEHAVIOR_REMOVE_LINK, + ) + + lookup = LookupAttributeMetadata( + schema_name="new_DepartmentId", + display_name=Label( + localized_labels=[ + LocalizedLabel(label="Department", language_code=1033) + ] + ), + ) + + relationship = OneToManyRelationshipMetadata( + schema_name="new_Department_Employee", + referenced_entity="new_department", + referencing_entity="new_employee", + referenced_attribute="new_departmentid", + cascade_configuration=CascadeConfiguration( + delete=CASCADE_BEHAVIOR_REMOVE_LINK, + ), + ) + + result = client.tables.create_one_to_many_relationship(lookup, relationship) + print(f"Created lookup field: {result.lookup_schema_name}") + + + + .. py:method:: create_many_to_many_relationship(relationship: PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata, *, solution: Optional[str] = None) -> PowerPlatform.Dataverse.models.relationship.RelationshipInfo + + Create a many-to-many relationship between tables. + + This operation creates a many-to-many relationship and an intersect + table to manage the relationship. + + :param relationship: Metadata defining the many-to-many relationship. + :type relationship: ~PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata + :param solution: Optional solution unique name to add relationship to. + :type solution: :class:`str` or None + + :return: Relationship metadata with ``relationship_id``, + ``relationship_schema_name``, ``relationship_type``, + ``entity1_logical_name``, and ``entity2_logical_name``. + :rtype: :class:`~PowerPlatform.Dataverse.models.relationship.RelationshipInfo` + + :raises ~PowerPlatform.Dataverse.core.errors.HttpError: + If the Web API request fails. + + .. rubric:: Example + + Create a many-to-many relationship: Employee <-> Project:: + + from PowerPlatform.Dataverse.models import ( + ManyToManyRelationshipMetadata, + ) + + relationship = ManyToManyRelationshipMetadata( + schema_name="new_employee_project", + entity1_logical_name="new_employee", + entity2_logical_name="new_project", + ) + + result = client.tables.create_many_to_many_relationship(relationship) + print(f"Created: {result.relationship_schema_name}") + + + + .. py:method:: delete_relationship(relationship_id: str) -> None + + Delete a relationship by its metadata ID. + + :param relationship_id: The GUID of the relationship metadata. + :type relationship_id: :class:`str` + + :raises ~PowerPlatform.Dataverse.core.errors.HttpError: + If the Web API request fails. + + .. warning:: + Deleting a relationship also removes the associated lookup attribute + for one-to-many relationships. This operation is irreversible. + + Example:: + + client.tables.delete_relationship( + "12345678-1234-1234-1234-123456789abc" + ) + + + + .. py:method:: get_relationship(schema_name: str) -> Optional[PowerPlatform.Dataverse.models.relationship.RelationshipInfo] + + Retrieve relationship metadata by schema name. + + :param schema_name: The schema name of the relationship. + :type schema_name: :class:`str` + + :return: Relationship metadata, or ``None`` if not found. + :rtype: :class:`~PowerPlatform.Dataverse.models.relationship.RelationshipInfo` + or None + + :raises ~PowerPlatform.Dataverse.core.errors.HttpError: + If the Web API request fails. + + Example:: + + rel = client.tables.get_relationship("new_Department_Employee") + if rel: + print(f"Found: {rel.relationship_schema_name}") + + + + .. py:method:: create_lookup_field(referencing_table: str, lookup_field_name: str, referenced_table: str, *, display_name: Optional[str] = None, description: Optional[str] = None, required: bool = False, cascade_delete: str = CASCADE_BEHAVIOR_REMOVE_LINK, solution: Optional[str] = None, language_code: int = 1033) -> PowerPlatform.Dataverse.models.relationship.RelationshipInfo + + Create a simple lookup field relationship. + + This is a convenience method that wraps :meth:`create_one_to_many_relationship` + for the common case of adding a lookup field to an existing table. + + :param referencing_table: Logical name of the table that will have + the lookup field (child table). + :type referencing_table: :class:`str` + :param lookup_field_name: Schema name for the lookup field + (e.g., ``"new_AccountId"``). + :type lookup_field_name: :class:`str` + :param referenced_table: Logical name of the table being referenced + (parent table). + :type referenced_table: :class:`str` + :param display_name: Display name for the lookup field. Defaults to + the referenced table name. + :type display_name: :class:`str` or None + :param description: Optional description for the lookup field. + :type description: :class:`str` or None + :param required: Whether the lookup is required. Defaults to ``False``. + :type required: :class:`bool` + :param cascade_delete: Delete behavior (``"RemoveLink"``, + ``"Cascade"``, ``"Restrict"``). Defaults to ``"RemoveLink"``. + :type cascade_delete: :class:`str` + :param solution: Optional solution unique name to add the relationship + to. + :type solution: :class:`str` or None + :param language_code: Language code for labels. Defaults to 1033 + (English). + :type language_code: :class:`int` + + :return: Relationship metadata with ``relationship_id``, + ``relationship_schema_name``, ``relationship_type``, + ``lookup_schema_name``, ``referenced_entity``, and + ``referencing_entity``. + :rtype: :class:`~PowerPlatform.Dataverse.models.relationship.RelationshipInfo` + + :raises ~PowerPlatform.Dataverse.core.errors.HttpError: + If the Web API request fails. + + .. rubric:: Example + + Create a simple lookup field:: + + result = client.tables.create_lookup_field( + referencing_table="new_order", + lookup_field_name="new_AccountId", + referenced_table="account", + display_name="Account", + required=True, + cascade_delete=CASCADE_BEHAVIOR_REMOVE_LINK, + ) + print(f"Created lookup: {result['lookup_schema_name']}") + + + + .. py:method:: create_alternate_key(table: str, key_name: str, columns: List[str], *, display_name: Optional[str] = None, language_code: int = 1033) -> PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo + + Create an alternate key on a table. + + Alternate keys allow upsert operations to identify records by one or + more columns instead of the primary GUID. After creation the key is + queued for index building; its :attr:`~PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo.status` will + transition from ``"Pending"`` to ``"Active"`` once the index is ready. + + :param table: Schema name of the table (e.g. ``"new_Product"``). + :type table: :class:`str` + :param key_name: Schema name for the new alternate key + (e.g. ``"new_product_code_key"``). + :type key_name: :class:`str` + :param columns: List of column logical names that compose the key + (e.g. ``["new_productcode"]``). + :type columns: list[str] + :param display_name: Display name for the key. Defaults to + ``key_name`` if not provided. + :type display_name: :class:`str` or None + :param language_code: Language code for labels. Defaults to 1033 + (English). + :type language_code: :class:`int` + + :return: Metadata for the newly created alternate key. + :rtype: :class:`~PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo` + + :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: + If the table does not exist. + :raises ~PowerPlatform.Dataverse.core.errors.HttpError: + If the Web API request fails. + + .. rubric:: Example + + Create a single-column alternate key for upsert:: + + key = client.tables.create_alternate_key( + "new_Product", + "new_product_code_key", + ["new_productcode"], + display_name="Product Code", + ) + print(f"Key ID: {key.metadata_id}") + print(f"Columns: {key.key_attributes}") + + + + .. py:method:: get_alternate_keys(table: str) -> List[PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo] + + List all alternate keys defined on a table. + + :param table: Schema name of the table (e.g. ``"new_Product"``). + :type table: :class:`str` + + :return: List of alternate key metadata objects. May be empty if no + alternate keys are defined. + :rtype: list[~PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo] + + :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: + If the table does not exist. + :raises ~PowerPlatform.Dataverse.core.errors.HttpError: + If the Web API request fails. + + .. rubric:: Example + + List alternate keys and print their status:: + + keys = client.tables.get_alternate_keys("new_Product") + for key in keys: + print(f"{key.schema_name}: {key.status}") + + + + .. py:method:: delete_alternate_key(table: str, key_id: str) -> None + + Delete an alternate key by its metadata ID. + + :param table: Schema name of the table (e.g. ``"new_Product"``). + :type table: :class:`str` + :param key_id: Metadata GUID of the alternate key to delete. + :type key_id: :class:`str` + + :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: + If the table does not exist. + :raises ~PowerPlatform.Dataverse.core.errors.HttpError: + If the Web API request fails. + + .. warning:: + Deleting an alternate key that is in use by upsert operations will + cause those operations to fail. This operation is irreversible. + + Example:: + + client.tables.delete_alternate_key( + "new_Product", + "12345678-1234-1234-1234-123456789abc", + ) + + + + .. py:method:: list_columns(table: str, *, select: Optional[List[str]] = None, filter: Optional[str] = None) -> List[Dict[str, Any]] + + List all attribute (column) definitions for a table. + + :param table: Schema name of the table (e.g. ``"account"`` or + ``"new_Product"``). + :type table: :class:`str` + :param select: Optional list of property names to project via + ``$select``. Values are passed as-is (PascalCase). + :type select: list[str] or None + :param filter: Optional OData ``$filter`` expression. For example, + ``"AttributeType eq 'String'"`` returns only string columns. + :type filter: :class:`str` or None + + :return: List of raw attribute metadata dictionaries. + :rtype: list[dict[str, typing.Any]] + + :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: + If the table is not found. + :raises ~PowerPlatform.Dataverse.core.errors.HttpError: + If the Web API request fails. + + Example:: + + # List all columns on the account table + columns = client.tables.list_columns("account") + for col in columns: + print(f"{col['LogicalName']} ({col.get('AttributeType')})") + + # List only specific properties + columns = client.tables.list_columns( + "account", + select=["LogicalName", "SchemaName", "AttributeType"], + ) + + # Filter to only string attributes + columns = client.tables.list_columns( + "account", + filter="AttributeType eq 'String'", + ) + + + + .. py:method:: list_relationships(*, filter: Optional[str] = None, select: Optional[List[str]] = None) -> List[Dict[str, Any]] + + List all relationship definitions in the environment. + + :param filter: Optional OData ``$filter`` expression. For example, + ``"RelationshipType eq Microsoft.Dynamics.CRM.RelationshipType'OneToManyRelationship'"`` + returns only one-to-many relationships. + :type filter: :class:`str` or None + :param select: Optional list of property names to project via + ``$select``. Values are passed as-is (PascalCase). + :type select: list[str] or None + + :return: List of raw relationship metadata dictionaries. + :rtype: list[dict[str, typing.Any]] + + :raises ~PowerPlatform.Dataverse.core.errors.HttpError: + If the Web API request fails. + + Example:: + + # List all relationships + rels = client.tables.list_relationships() + for rel in rels: + print(f"{rel['SchemaName']} ({rel.get('@odata.type')})") + + # Filter by type + one_to_many = client.tables.list_relationships( + filter="RelationshipType eq Microsoft.Dynamics.CRM.RelationshipType'OneToManyRelationship'" + ) + + # Select specific properties + rels = client.tables.list_relationships( + select=["SchemaName", "ReferencedEntity", "ReferencingEntity"] + ) + + + + .. py:method:: list_table_relationships(table: str, *, filter: Optional[str] = None, select: Optional[List[str]] = None) -> List[Dict[str, Any]] + + List all relationships for a specific table. + + Combines one-to-many, many-to-one, and many-to-many relationships + for the given table by querying + ``EntityDefinitions({id})/OneToManyRelationships``, + ``EntityDefinitions({id})/ManyToOneRelationships``, and + ``EntityDefinitions({id})/ManyToManyRelationships``. + + :param table: Schema name of the table (e.g. ``"account"``). + :type table: :class:`str` + :param filter: Optional OData ``$filter`` expression applied to each + sub-request. + :type filter: :class:`str` or None + :param select: Optional list of property names to project via + ``$select``. Values are passed as-is (PascalCase). + :type select: list[str] or None + + :return: Combined list of one-to-many, many-to-one, and many-to-many + relationship metadata dictionaries. + :rtype: list[dict[str, typing.Any]] + + :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: + If the table is not found. + :raises ~PowerPlatform.Dataverse.core.errors.HttpError: + If the Web API request fails. + + Example:: + + # List all relationships for the account table + rels = client.tables.list_table_relationships("account") + for rel in rels: + print(f"{rel['SchemaName']} -> {rel.get('@odata.type')}") + + + diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/utils/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/utils/index.rst.txt new file mode 100644 index 00000000..bf579628 --- /dev/null +++ b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/utils/index.rst.txt @@ -0,0 +1,13 @@ +PowerPlatform.Dataverse.utils +============================= + +.. py:module:: PowerPlatform.Dataverse.utils + +.. autoapi-nested-parse:: + + Utilities and adapters for the Dataverse SDK. + + Placeholder module for future utility adapters. + + + diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/index.rst.txt new file mode 100644 index 00000000..6bf6b5a5 --- /dev/null +++ b/docs_local/_build/_sources/autoapi/PowerPlatform/index.rst.txt @@ -0,0 +1,15 @@ +PowerPlatform +============= + +.. py:module:: PowerPlatform + + +Submodules +---------- + +.. toctree:: + :maxdepth: 1 + + /autoapi/PowerPlatform/Dataverse/index + + diff --git a/docs_local/_build/_sources/autoapi/index.rst.txt b/docs_local/_build/_sources/autoapi/index.rst.txt new file mode 100644 index 00000000..79277872 --- /dev/null +++ b/docs_local/_build/_sources/autoapi/index.rst.txt @@ -0,0 +1,11 @@ +API Reference +============= + +This page contains auto-generated API reference documentation [#f1]_. + +.. toctree:: + :titlesonly: + + /autoapi/PowerPlatform/index + +.. [#f1] Created with `sphinx-autoapi `_ \ No newline at end of file diff --git a/docs_local/_build/_sources/index.rst.txt b/docs_local/_build/_sources/index.rst.txt new file mode 100644 index 00000000..a3445f40 --- /dev/null +++ b/docs_local/_build/_sources/index.rst.txt @@ -0,0 +1,8 @@ +PowerPlatform Dataverse Client SDK +=================================== + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + autoapi/index diff --git a/docs_local/_build/_static/alabaster.css b/docs_local/_build/_static/alabaster.css new file mode 100644 index 00000000..e3174bf9 --- /dev/null +++ b/docs_local/_build/_static/alabaster.css @@ -0,0 +1,708 @@ +@import url("basic.css"); + +/* -- page layout ----------------------------------------------------------- */ + +body { + font-family: Georgia, serif; + font-size: 17px; + background-color: #fff; + color: #000; + margin: 0; + padding: 0; +} + + +div.document { + width: 940px; + margin: 30px auto 0 auto; +} + +div.documentwrapper { + float: left; + width: 100%; +} + +div.bodywrapper { + margin: 0 0 0 220px; +} + +div.sphinxsidebar { + width: 220px; + font-size: 14px; + line-height: 1.5; +} + +hr { + border: 1px solid #B1B4B6; +} + +div.body { + background-color: #fff; + color: #3E4349; + padding: 0 30px 0 30px; +} + +div.body > .section { + text-align: left; +} + +div.footer { + width: 940px; + margin: 20px auto 30px auto; + font-size: 14px; + color: #888; + text-align: right; +} + +div.footer a { + color: #888; +} + +p.caption { + font-family: inherit; + font-size: inherit; +} + + +div.relations { + display: none; +} + + +div.sphinxsidebar { + max-height: 100%; + overflow-y: auto; +} + +div.sphinxsidebar a { + color: #444; + text-decoration: none; + border-bottom: 1px dotted #999; +} + +div.sphinxsidebar a:hover { + border-bottom: 1px solid #999; +} + +div.sphinxsidebarwrapper { + padding: 18px 10px; +} + +div.sphinxsidebarwrapper p.logo { + padding: 0; + margin: -10px 0 0 0px; + text-align: center; +} + +div.sphinxsidebarwrapper h1.logo { + margin-top: -10px; + text-align: center; + margin-bottom: 5px; + text-align: left; +} + +div.sphinxsidebarwrapper h1.logo-name { + margin-top: 0px; +} + +div.sphinxsidebarwrapper p.blurb { + margin-top: 0; + font-style: normal; +} + +div.sphinxsidebar h3, +div.sphinxsidebar h4 { + font-family: Georgia, serif; + color: #444; + font-size: 24px; + font-weight: normal; + margin: 0 0 5px 0; + padding: 0; +} + +div.sphinxsidebar h4 { + font-size: 20px; +} + +div.sphinxsidebar h3 a { + color: #444; +} + +div.sphinxsidebar p.logo a, +div.sphinxsidebar h3 a, +div.sphinxsidebar p.logo a:hover, +div.sphinxsidebar h3 a:hover { + border: none; +} + +div.sphinxsidebar p { + color: #555; + margin: 10px 0; +} + +div.sphinxsidebar ul { + margin: 10px 0; + padding: 0; + color: #000; +} + +div.sphinxsidebar ul li.toctree-l1 > a { + font-size: 120%; +} + +div.sphinxsidebar ul li.toctree-l2 > a { + font-size: 110%; +} + +div.sphinxsidebar input { + border: 1px solid #CCC; + font-family: Georgia, serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox input[type="text"] { + width: 160px; +} + +div.sphinxsidebar .search > div { + display: table-cell; +} + +div.sphinxsidebar hr { + border: none; + height: 1px; + color: #AAA; + background: #AAA; + + text-align: left; + margin-left: 0; + width: 50%; +} + +div.sphinxsidebar .badge { + border-bottom: none; +} + +div.sphinxsidebar .badge:hover { + border-bottom: none; +} + +/* To address an issue with donation coming after search */ +div.sphinxsidebar h3.donation { + margin-top: 10px; +} + +/* -- body styles ----------------------------------------------------------- */ + +a { + color: #004B6B; + text-decoration: underline; +} + +a:hover { + color: #6D4100; + text-decoration: underline; +} + +div.body h1, +div.body h2, +div.body h3, +div.body h4, +div.body h5, +div.body h6 { + font-family: Georgia, serif; + font-weight: normal; + margin: 30px 0px 10px 0px; + padding: 0; +} + +div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; } +div.body h2 { font-size: 180%; } +div.body h3 { font-size: 150%; } +div.body h4 { font-size: 130%; } +div.body h5 { font-size: 100%; } +div.body h6 { font-size: 100%; } + +a.headerlink { + color: #DDD; + padding: 0 4px; + text-decoration: none; +} + +a.headerlink:hover { + color: #444; + background: #EAEAEA; +} + +div.body p, div.body dd, div.body li { + line-height: 1.4em; +} + +div.admonition { + margin: 20px 0px; + padding: 10px 30px; + background-color: #EEE; + border: 1px solid #CCC; +} + +div.admonition tt.xref, div.admonition code.xref, div.admonition a tt { + background-color: #FBFBFB; + border-bottom: 1px solid #fafafa; +} + +div.admonition p.admonition-title { + font-family: Georgia, serif; + font-weight: normal; + font-size: 24px; + margin: 0 0 10px 0; + padding: 0; + line-height: 1; +} + +div.admonition p.last { + margin-bottom: 0; +} + +div.highlight { + background-color: #fff; +} + +dt:target, .highlight { + background: #FAF3E8; +} + +div.warning { + background-color: #FCC; + border: 1px solid #FAA; +} + +div.danger { + background-color: #FCC; + border: 1px solid #FAA; + -moz-box-shadow: 2px 2px 4px #D52C2C; + -webkit-box-shadow: 2px 2px 4px #D52C2C; + box-shadow: 2px 2px 4px #D52C2C; +} + +div.error { + background-color: #FCC; + border: 1px solid #FAA; + -moz-box-shadow: 2px 2px 4px #D52C2C; + -webkit-box-shadow: 2px 2px 4px #D52C2C; + box-shadow: 2px 2px 4px #D52C2C; +} + +div.caution { + background-color: #FCC; + border: 1px solid #FAA; +} + +div.attention { + background-color: #FCC; + border: 1px solid #FAA; +} + +div.important { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.note { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.tip { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.hint { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.seealso { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.topic { + background-color: #EEE; +} + +p.admonition-title { + display: inline; +} + +p.admonition-title:after { + content: ":"; +} + +pre, tt, code { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; + font-size: 0.9em; +} + +.hll { + background-color: #FFC; + margin: 0 -12px; + padding: 0 12px; + display: block; +} + +img.screenshot { +} + +tt.descname, tt.descclassname, code.descname, code.descclassname { + font-size: 0.95em; +} + +tt.descname, code.descname { + padding-right: 0.08em; +} + +img.screenshot { + -moz-box-shadow: 2px 2px 4px #EEE; + -webkit-box-shadow: 2px 2px 4px #EEE; + box-shadow: 2px 2px 4px #EEE; +} + +table.docutils { + border: 1px solid #888; + -moz-box-shadow: 2px 2px 4px #EEE; + -webkit-box-shadow: 2px 2px 4px #EEE; + box-shadow: 2px 2px 4px #EEE; +} + +table.docutils td, table.docutils th { + border: 1px solid #888; + padding: 0.25em 0.7em; +} + +table.field-list, table.footnote { + border: none; + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; +} + +table.footnote { + margin: 15px 0; + width: 100%; + border: 1px solid #EEE; + background: #FDFDFD; + font-size: 0.9em; +} + +table.footnote + table.footnote { + margin-top: -15px; + border-top: none; +} + +table.field-list th { + padding: 0 0.8em 0 0; +} + +table.field-list td { + padding: 0; +} + +table.field-list p { + margin-bottom: 0.8em; +} + +/* Cloned from + * https://github.com/sphinx-doc/sphinx/commit/ef60dbfce09286b20b7385333d63a60321784e68 + */ +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +table.footnote td.label { + width: .1px; + padding: 0.3em 0 0.3em 0.5em; +} + +table.footnote td { + padding: 0.3em 0.5em; +} + +dl { + margin-left: 0; + margin-right: 0; + margin-top: 0; + padding: 0; +} + +dl dd { + margin-left: 30px; +} + +blockquote { + margin: 0 0 0 30px; + padding: 0; +} + +ul, ol { + /* Matches the 30px from the narrow-screen "li > ul" selector below */ + margin: 10px 0 10px 30px; + padding: 0; +} + +pre { + background: #EEE; + padding: 7px 30px; + margin: 15px 0px; + line-height: 1.3em; +} + +div.viewcode-block:target { + background: #ffd; +} + +dl pre, blockquote pre, li pre { + margin-left: 0; + padding-left: 30px; +} + +tt, code { + background-color: #ecf0f3; + color: #222; + /* padding: 1px 2px; */ +} + +tt.xref, code.xref, a tt { + background-color: #FBFBFB; + border-bottom: 1px solid #fff; +} + +a.reference { + text-decoration: none; + border-bottom: 1px dotted #004B6B; +} + +/* Don't put an underline on images */ +a.image-reference, a.image-reference:hover { + border-bottom: none; +} + +a.reference:hover { + border-bottom: 1px solid #6D4100; +} + +a.footnote-reference { + text-decoration: none; + font-size: 0.7em; + vertical-align: top; + border-bottom: 1px dotted #004B6B; +} + +a.footnote-reference:hover { + border-bottom: 1px solid #6D4100; +} + +a:hover tt, a:hover code { + background: #EEE; +} + + +@media screen and (max-width: 870px) { + + div.sphinxsidebar { + display: none; + } + + div.document { + width: 100%; + + } + + div.documentwrapper { + margin-left: 0; + margin-top: 0; + margin-right: 0; + margin-bottom: 0; + } + + div.bodywrapper { + margin-top: 0; + margin-right: 0; + margin-bottom: 0; + margin-left: 0; + } + + ul { + margin-left: 0; + } + + li > ul { + /* Matches the 30px from the "ul, ol" selector above */ + margin-left: 30px; + } + + .document { + width: auto; + } + + .footer { + width: auto; + } + + .bodywrapper { + margin: 0; + } + + .footer { + width: auto; + } + + .github { + display: none; + } + + + +} + + + +@media screen and (max-width: 875px) { + + body { + margin: 0; + padding: 20px 30px; + } + + div.documentwrapper { + float: none; + background: #fff; + } + + div.sphinxsidebar { + display: block; + float: none; + width: 102.5%; + margin: 50px -30px -20px -30px; + padding: 10px 20px; + background: #333; + color: #FFF; + } + + div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p, + div.sphinxsidebar h3 a { + color: #fff; + } + + div.sphinxsidebar a { + color: #AAA; + } + + div.sphinxsidebar p.logo { + display: none; + } + + div.document { + width: 100%; + margin: 0; + } + + div.footer { + display: none; + } + + div.bodywrapper { + margin: 0; + } + + div.body { + min-height: 0; + padding: 0; + } + + .rtd_doc_footer { + display: none; + } + + .document { + width: auto; + } + + .footer { + width: auto; + } + + .footer { + width: auto; + } + + .github { + display: none; + } +} + + +/* misc. */ + +.revsys-inline { + display: none!important; +} + +/* Hide ugly table cell borders in ..bibliography:: directive output */ +table.docutils.citation, table.docutils.citation td, table.docutils.citation th { + border: none; + /* Below needed in some edge cases; if not applied, bottom shadows appear */ + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; +} + + +/* relbar */ + +.related { + line-height: 30px; + width: 100%; + font-size: 0.9rem; +} + +.related.top { + border-bottom: 1px solid #EEE; + margin-bottom: 20px; +} + +.related.bottom { + border-top: 1px solid #EEE; +} + +.related ul { + padding: 0; + margin: 0; + list-style: none; +} + +.related li { + display: inline; +} + +nav#rellinks { + float: right; +} + +nav#rellinks li+li:before { + content: "|"; +} + +nav#breadcrumbs li+li:before { + content: "\00BB"; +} + +/* Hide certain items when printing */ +@media print { + div.related { + display: none; + } +} \ No newline at end of file diff --git a/docs_local/_build/_static/base-stemmer.js b/docs_local/_build/_static/base-stemmer.js new file mode 100644 index 00000000..e6fa0c49 --- /dev/null +++ b/docs_local/_build/_static/base-stemmer.js @@ -0,0 +1,476 @@ +// @ts-check + +/**@constructor*/ +BaseStemmer = function() { + /** @protected */ + this.current = ''; + this.cursor = 0; + this.limit = 0; + this.limit_backward = 0; + this.bra = 0; + this.ket = 0; + + /** + * @param {string} value + */ + this.setCurrent = function(value) { + this.current = value; + this.cursor = 0; + this.limit = this.current.length; + this.limit_backward = 0; + this.bra = this.cursor; + this.ket = this.limit; + }; + + /** + * @return {string} + */ + this.getCurrent = function() { + return this.current; + }; + + /** + * @param {BaseStemmer} other + */ + this.copy_from = function(other) { + /** @protected */ + this.current = other.current; + this.cursor = other.cursor; + this.limit = other.limit; + this.limit_backward = other.limit_backward; + this.bra = other.bra; + this.ket = other.ket; + }; + + /** + * @param {number[]} s + * @param {number} min + * @param {number} max + * @return {boolean} + */ + this.in_grouping = function(s, min, max) { + /** @protected */ + if (this.cursor >= this.limit) return false; + var ch = this.current.charCodeAt(this.cursor); + if (ch > max || ch < min) return false; + ch -= min; + if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0) return false; + this.cursor++; + return true; + }; + + /** + * @param {number[]} s + * @param {number} min + * @param {number} max + * @return {boolean} + */ + this.go_in_grouping = function(s, min, max) { + /** @protected */ + while (this.cursor < this.limit) { + var ch = this.current.charCodeAt(this.cursor); + if (ch > max || ch < min) + return true; + ch -= min; + if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0) + return true; + this.cursor++; + } + return false; + }; + + /** + * @param {number[]} s + * @param {number} min + * @param {number} max + * @return {boolean} + */ + this.in_grouping_b = function(s, min, max) { + /** @protected */ + if (this.cursor <= this.limit_backward) return false; + var ch = this.current.charCodeAt(this.cursor - 1); + if (ch > max || ch < min) return false; + ch -= min; + if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0) return false; + this.cursor--; + return true; + }; + + /** + * @param {number[]} s + * @param {number} min + * @param {number} max + * @return {boolean} + */ + this.go_in_grouping_b = function(s, min, max) { + /** @protected */ + while (this.cursor > this.limit_backward) { + var ch = this.current.charCodeAt(this.cursor - 1); + if (ch > max || ch < min) return true; + ch -= min; + if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0) return true; + this.cursor--; + } + return false; + }; + + /** + * @param {number[]} s + * @param {number} min + * @param {number} max + * @return {boolean} + */ + this.out_grouping = function(s, min, max) { + /** @protected */ + if (this.cursor >= this.limit) return false; + var ch = this.current.charCodeAt(this.cursor); + if (ch > max || ch < min) { + this.cursor++; + return true; + } + ch -= min; + if ((s[ch >>> 3] & (0X1 << (ch & 0x7))) == 0) { + this.cursor++; + return true; + } + return false; + }; + + /** + * @param {number[]} s + * @param {number} min + * @param {number} max + * @return {boolean} + */ + this.go_out_grouping = function(s, min, max) { + /** @protected */ + while (this.cursor < this.limit) { + var ch = this.current.charCodeAt(this.cursor); + if (ch <= max && ch >= min) { + ch -= min; + if ((s[ch >>> 3] & (0X1 << (ch & 0x7))) != 0) { + return true; + } + } + this.cursor++; + } + return false; + }; + + /** + * @param {number[]} s + * @param {number} min + * @param {number} max + * @return {boolean} + */ + this.out_grouping_b = function(s, min, max) { + /** @protected */ + if (this.cursor <= this.limit_backward) return false; + var ch = this.current.charCodeAt(this.cursor - 1); + if (ch > max || ch < min) { + this.cursor--; + return true; + } + ch -= min; + if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0) { + this.cursor--; + return true; + } + return false; + }; + + /** + * @param {number[]} s + * @param {number} min + * @param {number} max + * @return {boolean} + */ + this.go_out_grouping_b = function(s, min, max) { + /** @protected */ + while (this.cursor > this.limit_backward) { + var ch = this.current.charCodeAt(this.cursor - 1); + if (ch <= max && ch >= min) { + ch -= min; + if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) != 0) { + return true; + } + } + this.cursor--; + } + return false; + }; + + /** + * @param {string} s + * @return {boolean} + */ + this.eq_s = function(s) + { + /** @protected */ + if (this.limit - this.cursor < s.length) return false; + if (this.current.slice(this.cursor, this.cursor + s.length) != s) + { + return false; + } + this.cursor += s.length; + return true; + }; + + /** + * @param {string} s + * @return {boolean} + */ + this.eq_s_b = function(s) + { + /** @protected */ + if (this.cursor - this.limit_backward < s.length) return false; + if (this.current.slice(this.cursor - s.length, this.cursor) != s) + { + return false; + } + this.cursor -= s.length; + return true; + }; + + /** + * @param {Among[]} v + * @return {number} + */ + this.find_among = function(v) + { + /** @protected */ + var i = 0; + var j = v.length; + + var c = this.cursor; + var l = this.limit; + + var common_i = 0; + var common_j = 0; + + var first_key_inspected = false; + + while (true) + { + var k = i + ((j - i) >>> 1); + var diff = 0; + var common = common_i < common_j ? common_i : common_j; // smaller + // w[0]: string, w[1]: substring_i, w[2]: result, w[3]: function (optional) + var w = v[k]; + var i2; + for (i2 = common; i2 < w[0].length; i2++) + { + if (c + common == l) + { + diff = -1; + break; + } + diff = this.current.charCodeAt(c + common) - w[0].charCodeAt(i2); + if (diff != 0) break; + common++; + } + if (diff < 0) + { + j = k; + common_j = common; + } + else + { + i = k; + common_i = common; + } + if (j - i <= 1) + { + if (i > 0) break; // v->s has been inspected + if (j == i) break; // only one item in v + + // - but now we need to go round once more to get + // v->s inspected. This looks messy, but is actually + // the optimal approach. + + if (first_key_inspected) break; + first_key_inspected = true; + } + } + do { + var w = v[i]; + if (common_i >= w[0].length) + { + this.cursor = c + w[0].length; + if (w.length < 4) return w[2]; + var res = w[3](this); + this.cursor = c + w[0].length; + if (res) return w[2]; + } + i = w[1]; + } while (i >= 0); + return 0; + }; + + // find_among_b is for backwards processing. Same comments apply + /** + * @param {Among[]} v + * @return {number} + */ + this.find_among_b = function(v) + { + /** @protected */ + var i = 0; + var j = v.length + + var c = this.cursor; + var lb = this.limit_backward; + + var common_i = 0; + var common_j = 0; + + var first_key_inspected = false; + + while (true) + { + var k = i + ((j - i) >> 1); + var diff = 0; + var common = common_i < common_j ? common_i : common_j; + var w = v[k]; + var i2; + for (i2 = w[0].length - 1 - common; i2 >= 0; i2--) + { + if (c - common == lb) + { + diff = -1; + break; + } + diff = this.current.charCodeAt(c - 1 - common) - w[0].charCodeAt(i2); + if (diff != 0) break; + common++; + } + if (diff < 0) + { + j = k; + common_j = common; + } + else + { + i = k; + common_i = common; + } + if (j - i <= 1) + { + if (i > 0) break; + if (j == i) break; + if (first_key_inspected) break; + first_key_inspected = true; + } + } + do { + var w = v[i]; + if (common_i >= w[0].length) + { + this.cursor = c - w[0].length; + if (w.length < 4) return w[2]; + var res = w[3](this); + this.cursor = c - w[0].length; + if (res) return w[2]; + } + i = w[1]; + } while (i >= 0); + return 0; + }; + + /* to replace chars between c_bra and c_ket in this.current by the + * chars in s. + */ + /** + * @param {number} c_bra + * @param {number} c_ket + * @param {string} s + * @return {number} + */ + this.replace_s = function(c_bra, c_ket, s) + { + /** @protected */ + var adjustment = s.length - (c_ket - c_bra); + this.current = this.current.slice(0, c_bra) + s + this.current.slice(c_ket); + this.limit += adjustment; + if (this.cursor >= c_ket) this.cursor += adjustment; + else if (this.cursor > c_bra) this.cursor = c_bra; + return adjustment; + }; + + /** + * @return {boolean} + */ + this.slice_check = function() + { + /** @protected */ + if (this.bra < 0 || + this.bra > this.ket || + this.ket > this.limit || + this.limit > this.current.length) + { + return false; + } + return true; + }; + + /** + * @param {number} c_bra + * @return {boolean} + */ + this.slice_from = function(s) + { + /** @protected */ + var result = false; + if (this.slice_check()) + { + this.replace_s(this.bra, this.ket, s); + result = true; + } + return result; + }; + + /** + * @return {boolean} + */ + this.slice_del = function() + { + /** @protected */ + return this.slice_from(""); + }; + + /** + * @param {number} c_bra + * @param {number} c_ket + * @param {string} s + */ + this.insert = function(c_bra, c_ket, s) + { + /** @protected */ + var adjustment = this.replace_s(c_bra, c_ket, s); + if (c_bra <= this.bra) this.bra += adjustment; + if (c_bra <= this.ket) this.ket += adjustment; + }; + + /** + * @return {string} + */ + this.slice_to = function() + { + /** @protected */ + var result = ''; + if (this.slice_check()) + { + result = this.current.slice(this.bra, this.ket); + } + return result; + }; + + /** + * @return {string} + */ + this.assign_to = function() + { + /** @protected */ + return this.current.slice(0, this.limit); + }; +}; diff --git a/docs_local/_build/_static/basic.css b/docs_local/_build/_static/basic.css new file mode 100644 index 00000000..0028826d --- /dev/null +++ b/docs_local/_build/_static/basic.css @@ -0,0 +1,906 @@ +/* + * Sphinx stylesheet -- basic theme. + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +div.section::after { + display: block; + content: ''; + clear: left; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox form.search { + overflow: hidden; +} + +div.sphinxsidebar #searchbox input[type="text"] { + float: left; + width: 80%; + padding: 0.25em; + box-sizing: border-box; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + float: left; + width: 20%; + border-left: none; + padding: 0.25em; + box-sizing: border-box; +} + + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin-top: 10px; +} + +ul.search li { + padding: 5px 0; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li p.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body { + min-width: inherit; + max-width: 800px; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +a:visited { + color: #551A8B; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, figure.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, figure.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, figure.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +img.align-default, figure.align-default, .figure.align-default { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-default { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar, +aside.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px; + background-color: #ffe; + width: 40%; + float: right; + clear: right; + overflow-x: auto; +} + +p.sidebar-title { + font-weight: bold; +} + +nav.contents, +aside.topic, +div.admonition, div.topic, blockquote { + clear: left; +} + +/* -- topics ---------------------------------------------------------------- */ + +nav.contents, +aside.topic, +div.topic { + border: 1px solid #ccc; + padding: 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- content of sidebars/topics/admonitions -------------------------------- */ + +div.sidebar > :last-child, +aside.sidebar > :last-child, +nav.contents > :last-child, +aside.topic > :last-child, +div.topic > :last-child, +div.admonition > :last-child { + margin-bottom: 0; +} + +div.sidebar::after, +aside.sidebar::after, +nav.contents::after, +aside.topic::after, +div.topic::after, +div.admonition::after, +blockquote::after { + display: block; + content: ''; + clear: both; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + margin-top: 10px; + margin-bottom: 10px; + border: 0; + border-collapse: collapse; +} + +table.align-center { + margin-left: auto; + margin-right: auto; +} + +table.align-default { + margin-left: auto; + margin-right: auto; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +th > :first-child, +td > :first-child { + margin-top: 0px; +} + +th > :last-child, +td > :last-child { + margin-bottom: 0px; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure, figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption, figcaption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number, +figcaption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text, +figcaption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +/* -- hlist styles ---------------------------------------------------------- */ + +table.hlist { + margin: 1em 0; +} + +table.hlist td { + vertical-align: top; +} + +/* -- object description styles --------------------------------------------- */ + +.sig { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; +} + +.sig-name, code.descname { + background-color: transparent; + font-weight: bold; +} + +.sig-name { + font-size: 1.1em; +} + +code.descname { + font-size: 1.2em; +} + +.sig-prename, code.descclassname { + background-color: transparent; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.sig-param.n { + font-style: italic; +} + +/* C++ specific styling */ + +.sig-inline.c-texpr, +.sig-inline.cpp-texpr { + font-family: unset; +} + +.sig.c .k, .sig.c .kt, +.sig.cpp .k, .sig.cpp .kt { + color: #0033B3; +} + +.sig.c .m, +.sig.cpp .m { + color: #1750EB; +} + +.sig.c .s, .sig.c .sc, +.sig.cpp .s, .sig.cpp .sc { + color: #067D17; +} + + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +:not(li) > ol > li:first-child > :first-child, +:not(li) > ul > li:first-child > :first-child { + margin-top: 0px; +} + +:not(li) > ol > li:last-child > :last-child, +:not(li) > ul > li:last-child > :last-child { + margin-bottom: 0px; +} + +ol.simple ol p, +ol.simple ul p, +ul.simple ol p, +ul.simple ul p { + margin-top: 0; +} + +ol.simple > li:not(:first-child) > p, +ul.simple > li:not(:first-child) > p { + margin-top: 0; +} + +ol.simple p, +ul.simple p { + margin-bottom: 0; +} + +aside.footnote > span, +div.citation > span { + float: left; +} +aside.footnote > span:last-of-type, +div.citation > span:last-of-type { + padding-right: 0.5em; +} +aside.footnote > p { + margin-left: 2em; +} +div.citation > p { + margin-left: 4em; +} +aside.footnote > p:last-of-type, +div.citation > p:last-of-type { + margin-bottom: 0em; +} +aside.footnote > p:last-of-type:after, +div.citation > p:last-of-type:after { + content: ""; + clear: both; +} + +dl.field-list { + display: grid; + grid-template-columns: fit-content(30%) auto; +} + +dl.field-list > dt { + font-weight: bold; + word-break: break-word; + padding-left: 0.5em; + padding-right: 5px; +} + +dl.field-list > dd { + padding-left: 0.5em; + margin-top: 0em; + margin-left: 0em; + margin-bottom: 0em; +} + +dl { + margin-bottom: 15px; +} + +dd > :first-child { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +.sig dd { + margin-top: 0px; + margin-bottom: 0px; +} + +.sig dl { + margin-top: 0px; + margin-bottom: 0px; +} + +dl > dd:last-child, +dl > dd:last-child > :last-child { + margin-bottom: 0; +} + +dt:target, span.highlighted { + background-color: #fbe54e; +} + +rect.highlighted { + fill: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +.classifier:before { + font-style: normal; + margin: 0 0.5em; + content: ":"; + display: inline-block; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +pre, div[class*="highlight-"] { + clear: both; +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; + white-space: nowrap; +} + +div[class*="highlight-"] { + margin: 1em 0; +} + +td.linenos pre { + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + display: block; +} + +table.highlighttable tbody { + display: block; +} + +table.highlighttable tr { + display: flex; +} + +table.highlighttable td { + margin: 0; + padding: 0; +} + +table.highlighttable td.linenos { + padding-right: 0.5em; +} + +table.highlighttable td.code { + flex: 1; + overflow: hidden; +} + +.highlight .hll { + display: block; +} + +div.highlight pre, +table.highlighttable pre { + margin: 0; +} + +div.code-block-caption + div { + margin-top: 0; +} + +div.code-block-caption { + margin-top: 1em; + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +table.highlighttable td.linenos, +span.linenos, +div.highlight span.gp { /* gp: Generic.Prompt */ + user-select: none; + -webkit-user-select: text; /* Safari fallback only */ + -webkit-user-select: none; /* Chrome/Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE10+ */ +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + margin: 1em 0; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: absolute; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/docs_local/_build/_static/custom.css b/docs_local/_build/_static/custom.css new file mode 100644 index 00000000..2a924f1d --- /dev/null +++ b/docs_local/_build/_static/custom.css @@ -0,0 +1 @@ +/* This file intentionally left blank. */ diff --git a/docs_local/_build/_static/doctools.js b/docs_local/_build/_static/doctools.js new file mode 100644 index 00000000..807cdb17 --- /dev/null +++ b/docs_local/_build/_static/doctools.js @@ -0,0 +1,150 @@ +/* + * Base JavaScript utilities for all Sphinx HTML documentation. + */ +"use strict"; + +const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ + "TEXTAREA", + "INPUT", + "SELECT", + "BUTTON", +]); + +const _ready = (callback) => { + if (document.readyState !== "loading") { + callback(); + } else { + document.addEventListener("DOMContentLoaded", callback); + } +}; + +/** + * Small JavaScript module for the documentation. + */ +const Documentation = { + init: () => { + Documentation.initDomainIndexTable(); + Documentation.initOnKeyListeners(); + }, + + /** + * i18n support + */ + TRANSLATIONS: {}, + PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), + LOCALE: "unknown", + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext: (string) => { + const translated = Documentation.TRANSLATIONS[string]; + switch (typeof translated) { + case "undefined": + return string; // no translation + case "string": + return translated; // translation exists + default: + return translated[0]; // (singular, plural) translation tuple exists + } + }, + + ngettext: (singular, plural, n) => { + const translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated !== "undefined") + return translated[Documentation.PLURAL_EXPR(n)]; + return n === 1 ? singular : plural; + }, + + addTranslations: (catalog) => { + Object.assign(Documentation.TRANSLATIONS, catalog.messages); + Documentation.PLURAL_EXPR = new Function( + "n", + `return (${catalog.plural_expr})`, + ); + Documentation.LOCALE = catalog.locale; + }, + + /** + * helper function to focus on search bar + */ + focusSearchBar: () => { + document.querySelectorAll("input[name=q]")[0]?.focus(); + }, + + /** + * Initialise the domain index toggle buttons + */ + initDomainIndexTable: () => { + const toggler = (el) => { + const idNumber = el.id.substr(7); + const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); + if (el.src.substr(-9) === "minus.png") { + el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; + toggledRows.forEach((el) => (el.style.display = "none")); + } else { + el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; + toggledRows.forEach((el) => (el.style.display = "")); + } + }; + + const togglerElements = document.querySelectorAll("img.toggler"); + togglerElements.forEach((el) => + el.addEventListener("click", (event) => toggler(event.currentTarget)), + ); + togglerElements.forEach((el) => (el.style.display = "")); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); + }, + + initOnKeyListeners: () => { + // only install a listener if it is really needed + if ( + !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS + && !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS + ) + return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) + return; + // bail with special keys + if (event.altKey || event.ctrlKey || event.metaKey) return; + + if (!event.shiftKey) { + switch (event.key) { + case "ArrowLeft": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const prevLink = document.querySelector('link[rel="prev"]'); + if (prevLink && prevLink.href) { + window.location.href = prevLink.href; + event.preventDefault(); + } + break; + case "ArrowRight": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const nextLink = document.querySelector('link[rel="next"]'); + if (nextLink && nextLink.href) { + window.location.href = nextLink.href; + event.preventDefault(); + } + break; + } + } + + // some keyboard layouts may need Shift to get / + switch (event.key) { + case "/": + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; + Documentation.focusSearchBar(); + event.preventDefault(); + } + }); + }, +}; + +// quick alias for translations +const _ = Documentation.gettext; + +_ready(Documentation.init); diff --git a/docs_local/_build/_static/documentation_options.js b/docs_local/_build/_static/documentation_options.js new file mode 100644 index 00000000..e8b7cc1a --- /dev/null +++ b/docs_local/_build/_static/documentation_options.js @@ -0,0 +1,13 @@ +const DOCUMENTATION_OPTIONS = { + VERSION: '0.1.0b11', + LANGUAGE: 'en', + COLLAPSE_INDEX: false, + BUILDER: 'html', + FILE_SUFFIX: '.html', + LINK_SUFFIX: '.html', + HAS_SOURCE: true, + SOURCELINK_SUFFIX: '.txt', + NAVIGATION_WITH_KEYS: false, + SHOW_SEARCH_SUMMARY: true, + ENABLE_SEARCH_SHORTCUTS: true, +}; \ No newline at end of file diff --git a/docs_local/_build/_static/english-stemmer.js b/docs_local/_build/_static/english-stemmer.js new file mode 100644 index 00000000..056760ee --- /dev/null +++ b/docs_local/_build/_static/english-stemmer.js @@ -0,0 +1,1066 @@ +// Generated from english.sbl by Snowball 3.0.1 - https://snowballstem.org/ + +/**@constructor*/ +var EnglishStemmer = function() { + var base = new BaseStemmer(); + + /** @const */ var a_0 = [ + ["arsen", -1, -1], + ["commun", -1, -1], + ["emerg", -1, -1], + ["gener", -1, -1], + ["later", -1, -1], + ["organ", -1, -1], + ["past", -1, -1], + ["univers", -1, -1] + ]; + + /** @const */ var a_1 = [ + ["'", -1, 1], + ["'s'", 0, 1], + ["'s", -1, 1] + ]; + + /** @const */ var a_2 = [ + ["ied", -1, 2], + ["s", -1, 3], + ["ies", 1, 2], + ["sses", 1, 1], + ["ss", 1, -1], + ["us", 1, -1] + ]; + + /** @const */ var a_3 = [ + ["succ", -1, 1], + ["proc", -1, 1], + ["exc", -1, 1] + ]; + + /** @const */ var a_4 = [ + ["even", -1, 2], + ["cann", -1, 2], + ["inn", -1, 2], + ["earr", -1, 2], + ["herr", -1, 2], + ["out", -1, 2], + ["y", -1, 1] + ]; + + /** @const */ var a_5 = [ + ["", -1, -1], + ["ed", 0, 2], + ["eed", 1, 1], + ["ing", 0, 3], + ["edly", 0, 2], + ["eedly", 4, 1], + ["ingly", 0, 2] + ]; + + /** @const */ var a_6 = [ + ["", -1, 3], + ["bb", 0, 2], + ["dd", 0, 2], + ["ff", 0, 2], + ["gg", 0, 2], + ["bl", 0, 1], + ["mm", 0, 2], + ["nn", 0, 2], + ["pp", 0, 2], + ["rr", 0, 2], + ["at", 0, 1], + ["tt", 0, 2], + ["iz", 0, 1] + ]; + + /** @const */ var a_7 = [ + ["anci", -1, 3], + ["enci", -1, 2], + ["ogi", -1, 14], + ["li", -1, 16], + ["bli", 3, 12], + ["abli", 4, 4], + ["alli", 3, 8], + ["fulli", 3, 9], + ["lessli", 3, 15], + ["ousli", 3, 10], + ["entli", 3, 5], + ["aliti", -1, 8], + ["biliti", -1, 12], + ["iviti", -1, 11], + ["tional", -1, 1], + ["ational", 14, 7], + ["alism", -1, 8], + ["ation", -1, 7], + ["ization", 17, 6], + ["izer", -1, 6], + ["ator", -1, 7], + ["iveness", -1, 11], + ["fulness", -1, 9], + ["ousness", -1, 10], + ["ogist", -1, 13] + ]; + + /** @const */ var a_8 = [ + ["icate", -1, 4], + ["ative", -1, 6], + ["alize", -1, 3], + ["iciti", -1, 4], + ["ical", -1, 4], + ["tional", -1, 1], + ["ational", 5, 2], + ["ful", -1, 5], + ["ness", -1, 5] + ]; + + /** @const */ var a_9 = [ + ["ic", -1, 1], + ["ance", -1, 1], + ["ence", -1, 1], + ["able", -1, 1], + ["ible", -1, 1], + ["ate", -1, 1], + ["ive", -1, 1], + ["ize", -1, 1], + ["iti", -1, 1], + ["al", -1, 1], + ["ism", -1, 1], + ["ion", -1, 2], + ["er", -1, 1], + ["ous", -1, 1], + ["ant", -1, 1], + ["ent", -1, 1], + ["ment", 15, 1], + ["ement", 16, 1] + ]; + + /** @const */ var a_10 = [ + ["e", -1, 1], + ["l", -1, 2] + ]; + + /** @const */ var a_11 = [ + ["andes", -1, -1], + ["atlas", -1, -1], + ["bias", -1, -1], + ["cosmos", -1, -1], + ["early", -1, 5], + ["gently", -1, 3], + ["howe", -1, -1], + ["idly", -1, 2], + ["news", -1, -1], + ["only", -1, 6], + ["singly", -1, 7], + ["skies", -1, 1], + ["sky", -1, -1], + ["ugly", -1, 4] + ]; + + /** @const */ var /** Array */ g_aeo = [17, 64]; + + /** @const */ var /** Array */ g_v = [17, 65, 16, 1]; + + /** @const */ var /** Array */ g_v_WXY = [1, 17, 65, 208, 1]; + + /** @const */ var /** Array */ g_valid_LI = [55, 141, 2]; + + var /** boolean */ B_Y_found = false; + var /** number */ I_p2 = 0; + var /** number */ I_p1 = 0; + + + /** @return {boolean} */ + function r_prelude() { + B_Y_found = false; + /** @const */ var /** number */ v_1 = base.cursor; + lab0: { + base.bra = base.cursor; + if (!(base.eq_s("'"))) + { + break lab0; + } + base.ket = base.cursor; + if (!base.slice_del()) + { + return false; + } + } + base.cursor = v_1; + /** @const */ var /** number */ v_2 = base.cursor; + lab1: { + base.bra = base.cursor; + if (!(base.eq_s("y"))) + { + break lab1; + } + base.ket = base.cursor; + if (!base.slice_from("Y")) + { + return false; + } + B_Y_found = true; + } + base.cursor = v_2; + /** @const */ var /** number */ v_3 = base.cursor; + lab2: { + while(true) + { + /** @const */ var /** number */ v_4 = base.cursor; + lab3: { + golab4: while(true) + { + /** @const */ var /** number */ v_5 = base.cursor; + lab5: { + if (!(base.in_grouping(g_v, 97, 121))) + { + break lab5; + } + base.bra = base.cursor; + if (!(base.eq_s("y"))) + { + break lab5; + } + base.ket = base.cursor; + base.cursor = v_5; + break golab4; + } + base.cursor = v_5; + if (base.cursor >= base.limit) + { + break lab3; + } + base.cursor++; + } + if (!base.slice_from("Y")) + { + return false; + } + B_Y_found = true; + continue; + } + base.cursor = v_4; + break; + } + } + base.cursor = v_3; + return true; + }; + + /** @return {boolean} */ + function r_mark_regions() { + I_p1 = base.limit; + I_p2 = base.limit; + /** @const */ var /** number */ v_1 = base.cursor; + lab0: { + lab1: { + /** @const */ var /** number */ v_2 = base.cursor; + lab2: { + if (base.find_among(a_0) == 0) + { + break lab2; + } + break lab1; + } + base.cursor = v_2; + if (!base.go_out_grouping(g_v, 97, 121)) + { + break lab0; + } + base.cursor++; + if (!base.go_in_grouping(g_v, 97, 121)) + { + break lab0; + } + base.cursor++; + } + I_p1 = base.cursor; + if (!base.go_out_grouping(g_v, 97, 121)) + { + break lab0; + } + base.cursor++; + if (!base.go_in_grouping(g_v, 97, 121)) + { + break lab0; + } + base.cursor++; + I_p2 = base.cursor; + } + base.cursor = v_1; + return true; + }; + + /** @return {boolean} */ + function r_shortv() { + lab0: { + /** @const */ var /** number */ v_1 = base.limit - base.cursor; + lab1: { + if (!(base.out_grouping_b(g_v_WXY, 89, 121))) + { + break lab1; + } + if (!(base.in_grouping_b(g_v, 97, 121))) + { + break lab1; + } + if (!(base.out_grouping_b(g_v, 97, 121))) + { + break lab1; + } + break lab0; + } + base.cursor = base.limit - v_1; + lab2: { + if (!(base.out_grouping_b(g_v, 97, 121))) + { + break lab2; + } + if (!(base.in_grouping_b(g_v, 97, 121))) + { + break lab2; + } + if (base.cursor > base.limit_backward) + { + break lab2; + } + break lab0; + } + base.cursor = base.limit - v_1; + if (!(base.eq_s_b("past"))) + { + return false; + } + } + return true; + }; + + /** @return {boolean} */ + function r_R1() { + return I_p1 <= base.cursor; + }; + + /** @return {boolean} */ + function r_R2() { + return I_p2 <= base.cursor; + }; + + /** @return {boolean} */ + function r_Step_1a() { + var /** number */ among_var; + /** @const */ var /** number */ v_1 = base.limit - base.cursor; + lab0: { + base.ket = base.cursor; + if (base.find_among_b(a_1) == 0) + { + base.cursor = base.limit - v_1; + break lab0; + } + base.bra = base.cursor; + if (!base.slice_del()) + { + return false; + } + } + base.ket = base.cursor; + among_var = base.find_among_b(a_2); + if (among_var == 0) + { + return false; + } + base.bra = base.cursor; + switch (among_var) { + case 1: + if (!base.slice_from("ss")) + { + return false; + } + break; + case 2: + lab1: { + /** @const */ var /** number */ v_2 = base.limit - base.cursor; + lab2: { + { + /** @const */ var /** number */ c1 = base.cursor - 2; + if (c1 < base.limit_backward) + { + break lab2; + } + base.cursor = c1; + } + if (!base.slice_from("i")) + { + return false; + } + break lab1; + } + base.cursor = base.limit - v_2; + if (!base.slice_from("ie")) + { + return false; + } + } + break; + case 3: + if (base.cursor <= base.limit_backward) + { + return false; + } + base.cursor--; + if (!base.go_out_grouping_b(g_v, 97, 121)) + { + return false; + } + base.cursor--; + if (!base.slice_del()) + { + return false; + } + break; + } + return true; + }; + + /** @return {boolean} */ + function r_Step_1b() { + var /** number */ among_var; + base.ket = base.cursor; + among_var = base.find_among_b(a_5); + base.bra = base.cursor; + lab0: { + /** @const */ var /** number */ v_1 = base.limit - base.cursor; + lab1: { + switch (among_var) { + case 1: + /** @const */ var /** number */ v_2 = base.limit - base.cursor; + lab2: { + lab3: { + /** @const */ var /** number */ v_3 = base.limit - base.cursor; + lab4: { + if (base.find_among_b(a_3) == 0) + { + break lab4; + } + if (base.cursor > base.limit_backward) + { + break lab4; + } + break lab3; + } + base.cursor = base.limit - v_3; + if (!r_R1()) + { + break lab2; + } + if (!base.slice_from("ee")) + { + return false; + } + } + } + base.cursor = base.limit - v_2; + break; + case 2: + break lab1; + case 3: + among_var = base.find_among_b(a_4); + if (among_var == 0) + { + break lab1; + } + switch (among_var) { + case 1: + /** @const */ var /** number */ v_4 = base.limit - base.cursor; + if (!(base.out_grouping_b(g_v, 97, 121))) + { + break lab1; + } + if (base.cursor > base.limit_backward) + { + break lab1; + } + base.cursor = base.limit - v_4; + base.bra = base.cursor; + if (!base.slice_from("ie")) + { + return false; + } + break; + case 2: + if (base.cursor > base.limit_backward) + { + break lab1; + } + break; + } + break; + } + break lab0; + } + base.cursor = base.limit - v_1; + /** @const */ var /** number */ v_5 = base.limit - base.cursor; + if (!base.go_out_grouping_b(g_v, 97, 121)) + { + return false; + } + base.cursor--; + base.cursor = base.limit - v_5; + if (!base.slice_del()) + { + return false; + } + base.ket = base.cursor; + base.bra = base.cursor; + /** @const */ var /** number */ v_6 = base.limit - base.cursor; + among_var = base.find_among_b(a_6); + switch (among_var) { + case 1: + if (!base.slice_from("e")) + { + return false; + } + return false; + case 2: + { + /** @const */ var /** number */ v_7 = base.limit - base.cursor; + lab5: { + if (!(base.in_grouping_b(g_aeo, 97, 111))) + { + break lab5; + } + if (base.cursor > base.limit_backward) + { + break lab5; + } + return false; + } + base.cursor = base.limit - v_7; + } + break; + case 3: + if (base.cursor != I_p1) + { + return false; + } + /** @const */ var /** number */ v_8 = base.limit - base.cursor; + if (!r_shortv()) + { + return false; + } + base.cursor = base.limit - v_8; + if (!base.slice_from("e")) + { + return false; + } + return false; + } + base.cursor = base.limit - v_6; + base.ket = base.cursor; + if (base.cursor <= base.limit_backward) + { + return false; + } + base.cursor--; + base.bra = base.cursor; + if (!base.slice_del()) + { + return false; + } + } + return true; + }; + + /** @return {boolean} */ + function r_Step_1c() { + base.ket = base.cursor; + lab0: { + /** @const */ var /** number */ v_1 = base.limit - base.cursor; + lab1: { + if (!(base.eq_s_b("y"))) + { + break lab1; + } + break lab0; + } + base.cursor = base.limit - v_1; + if (!(base.eq_s_b("Y"))) + { + return false; + } + } + base.bra = base.cursor; + if (!(base.out_grouping_b(g_v, 97, 121))) + { + return false; + } + lab2: { + if (base.cursor > base.limit_backward) + { + break lab2; + } + return false; + } + if (!base.slice_from("i")) + { + return false; + } + return true; + }; + + /** @return {boolean} */ + function r_Step_2() { + var /** number */ among_var; + base.ket = base.cursor; + among_var = base.find_among_b(a_7); + if (among_var == 0) + { + return false; + } + base.bra = base.cursor; + if (!r_R1()) + { + return false; + } + switch (among_var) { + case 1: + if (!base.slice_from("tion")) + { + return false; + } + break; + case 2: + if (!base.slice_from("ence")) + { + return false; + } + break; + case 3: + if (!base.slice_from("ance")) + { + return false; + } + break; + case 4: + if (!base.slice_from("able")) + { + return false; + } + break; + case 5: + if (!base.slice_from("ent")) + { + return false; + } + break; + case 6: + if (!base.slice_from("ize")) + { + return false; + } + break; + case 7: + if (!base.slice_from("ate")) + { + return false; + } + break; + case 8: + if (!base.slice_from("al")) + { + return false; + } + break; + case 9: + if (!base.slice_from("ful")) + { + return false; + } + break; + case 10: + if (!base.slice_from("ous")) + { + return false; + } + break; + case 11: + if (!base.slice_from("ive")) + { + return false; + } + break; + case 12: + if (!base.slice_from("ble")) + { + return false; + } + break; + case 13: + if (!base.slice_from("og")) + { + return false; + } + break; + case 14: + if (!(base.eq_s_b("l"))) + { + return false; + } + if (!base.slice_from("og")) + { + return false; + } + break; + case 15: + if (!base.slice_from("less")) + { + return false; + } + break; + case 16: + if (!(base.in_grouping_b(g_valid_LI, 99, 116))) + { + return false; + } + if (!base.slice_del()) + { + return false; + } + break; + } + return true; + }; + + /** @return {boolean} */ + function r_Step_3() { + var /** number */ among_var; + base.ket = base.cursor; + among_var = base.find_among_b(a_8); + if (among_var == 0) + { + return false; + } + base.bra = base.cursor; + if (!r_R1()) + { + return false; + } + switch (among_var) { + case 1: + if (!base.slice_from("tion")) + { + return false; + } + break; + case 2: + if (!base.slice_from("ate")) + { + return false; + } + break; + case 3: + if (!base.slice_from("al")) + { + return false; + } + break; + case 4: + if (!base.slice_from("ic")) + { + return false; + } + break; + case 5: + if (!base.slice_del()) + { + return false; + } + break; + case 6: + if (!r_R2()) + { + return false; + } + if (!base.slice_del()) + { + return false; + } + break; + } + return true; + }; + + /** @return {boolean} */ + function r_Step_4() { + var /** number */ among_var; + base.ket = base.cursor; + among_var = base.find_among_b(a_9); + if (among_var == 0) + { + return false; + } + base.bra = base.cursor; + if (!r_R2()) + { + return false; + } + switch (among_var) { + case 1: + if (!base.slice_del()) + { + return false; + } + break; + case 2: + lab0: { + /** @const */ var /** number */ v_1 = base.limit - base.cursor; + lab1: { + if (!(base.eq_s_b("s"))) + { + break lab1; + } + break lab0; + } + base.cursor = base.limit - v_1; + if (!(base.eq_s_b("t"))) + { + return false; + } + } + if (!base.slice_del()) + { + return false; + } + break; + } + return true; + }; + + /** @return {boolean} */ + function r_Step_5() { + var /** number */ among_var; + base.ket = base.cursor; + among_var = base.find_among_b(a_10); + if (among_var == 0) + { + return false; + } + base.bra = base.cursor; + switch (among_var) { + case 1: + lab0: { + lab1: { + if (!r_R2()) + { + break lab1; + } + break lab0; + } + if (!r_R1()) + { + return false; + } + { + /** @const */ var /** number */ v_1 = base.limit - base.cursor; + lab2: { + if (!r_shortv()) + { + break lab2; + } + return false; + } + base.cursor = base.limit - v_1; + } + } + if (!base.slice_del()) + { + return false; + } + break; + case 2: + if (!r_R2()) + { + return false; + } + if (!(base.eq_s_b("l"))) + { + return false; + } + if (!base.slice_del()) + { + return false; + } + break; + } + return true; + }; + + /** @return {boolean} */ + function r_exception1() { + var /** number */ among_var; + base.bra = base.cursor; + among_var = base.find_among(a_11); + if (among_var == 0) + { + return false; + } + base.ket = base.cursor; + if (base.cursor < base.limit) + { + return false; + } + switch (among_var) { + case 1: + if (!base.slice_from("sky")) + { + return false; + } + break; + case 2: + if (!base.slice_from("idl")) + { + return false; + } + break; + case 3: + if (!base.slice_from("gentl")) + { + return false; + } + break; + case 4: + if (!base.slice_from("ugli")) + { + return false; + } + break; + case 5: + if (!base.slice_from("earli")) + { + return false; + } + break; + case 6: + if (!base.slice_from("onli")) + { + return false; + } + break; + case 7: + if (!base.slice_from("singl")) + { + return false; + } + break; + } + return true; + }; + + /** @return {boolean} */ + function r_postlude() { + if (!B_Y_found) + { + return false; + } + while(true) + { + /** @const */ var /** number */ v_1 = base.cursor; + lab0: { + golab1: while(true) + { + /** @const */ var /** number */ v_2 = base.cursor; + lab2: { + base.bra = base.cursor; + if (!(base.eq_s("Y"))) + { + break lab2; + } + base.ket = base.cursor; + base.cursor = v_2; + break golab1; + } + base.cursor = v_2; + if (base.cursor >= base.limit) + { + break lab0; + } + base.cursor++; + } + if (!base.slice_from("y")) + { + return false; + } + continue; + } + base.cursor = v_1; + break; + } + return true; + }; + + this.stem = /** @return {boolean} */ function() { + lab0: { + /** @const */ var /** number */ v_1 = base.cursor; + lab1: { + if (!r_exception1()) + { + break lab1; + } + break lab0; + } + base.cursor = v_1; + lab2: { + { + /** @const */ var /** number */ v_2 = base.cursor; + lab3: { + { + /** @const */ var /** number */ c1 = base.cursor + 3; + if (c1 > base.limit) + { + break lab3; + } + base.cursor = c1; + } + break lab2; + } + base.cursor = v_2; + } + break lab0; + } + base.cursor = v_1; + r_prelude(); + r_mark_regions(); + base.limit_backward = base.cursor; base.cursor = base.limit; + /** @const */ var /** number */ v_3 = base.limit - base.cursor; + r_Step_1a(); + base.cursor = base.limit - v_3; + /** @const */ var /** number */ v_4 = base.limit - base.cursor; + r_Step_1b(); + base.cursor = base.limit - v_4; + /** @const */ var /** number */ v_5 = base.limit - base.cursor; + r_Step_1c(); + base.cursor = base.limit - v_5; + /** @const */ var /** number */ v_6 = base.limit - base.cursor; + r_Step_2(); + base.cursor = base.limit - v_6; + /** @const */ var /** number */ v_7 = base.limit - base.cursor; + r_Step_3(); + base.cursor = base.limit - v_7; + /** @const */ var /** number */ v_8 = base.limit - base.cursor; + r_Step_4(); + base.cursor = base.limit - v_8; + /** @const */ var /** number */ v_9 = base.limit - base.cursor; + r_Step_5(); + base.cursor = base.limit - v_9; + base.cursor = base.limit_backward; + /** @const */ var /** number */ v_10 = base.cursor; + r_postlude(); + base.cursor = v_10; + } + return true; + }; + + /**@return{string}*/ + this['stemWord'] = function(/**string*/word) { + base.setCurrent(word); + this.stem(); + return base.getCurrent(); + }; +}; diff --git a/docs_local/_build/_static/file.png b/docs_local/_build/_static/file.png new file mode 100644 index 0000000000000000000000000000000000000000..a858a410e4faa62ce324d814e4b816fff83a6fb3 GIT binary patch literal 286 zcmV+(0pb3MP)s`hMrGg#P~ix$^RISR_I47Y|r1 z_CyJOe}D1){SET-^Amu_i71Lt6eYfZjRyw@I6OQAIXXHDfiX^GbOlHe=Ae4>0m)d(f|Me07*qoM6N<$f}vM^LjV8( literal 0 HcmV?d00001 diff --git a/docs_local/_build/_static/graphviz.css b/docs_local/_build/_static/graphviz.css new file mode 100644 index 00000000..30f3837b --- /dev/null +++ b/docs_local/_build/_static/graphviz.css @@ -0,0 +1,12 @@ +/* + * Sphinx stylesheet -- graphviz extension. + */ + +img.graphviz { + border: 0; + max-width: 100%; +} + +object.graphviz { + max-width: 100%; +} diff --git a/docs_local/_build/_static/language_data.js b/docs_local/_build/_static/language_data.js new file mode 100644 index 00000000..57767864 --- /dev/null +++ b/docs_local/_build/_static/language_data.js @@ -0,0 +1,13 @@ +/* + * This script contains the language-specific data used by searchtools.js, + * namely the set of stopwords, stemmer, scorer and splitter. + */ + +const stopwords = new Set(["a", "about", "above", "after", "again", "against", "all", "am", "an", "and", "any", "are", "aren't", "as", "at", "be", "because", "been", "before", "being", "below", "between", "both", "but", "by", "can't", "cannot", "could", "couldn't", "did", "didn't", "do", "does", "doesn't", "doing", "don't", "down", "during", "each", "few", "for", "from", "further", "had", "hadn't", "has", "hasn't", "have", "haven't", "having", "he", "he'd", "he'll", "he's", "her", "here", "here's", "hers", "herself", "him", "himself", "his", "how", "how's", "i", "i'd", "i'll", "i'm", "i've", "if", "in", "into", "is", "isn't", "it", "it's", "its", "itself", "let's", "me", "more", "most", "mustn't", "my", "myself", "no", "nor", "not", "of", "off", "on", "once", "only", "or", "other", "ought", "our", "ours", "ourselves", "out", "over", "own", "same", "shan't", "she", "she'd", "she'll", "she's", "should", "shouldn't", "so", "some", "such", "than", "that", "that's", "the", "their", "theirs", "them", "themselves", "then", "there", "there's", "these", "they", "they'd", "they'll", "they're", "they've", "this", "those", "through", "to", "too", "under", "until", "up", "very", "was", "wasn't", "we", "we'd", "we'll", "we're", "we've", "were", "weren't", "what", "what's", "when", "when's", "where", "where's", "which", "while", "who", "who's", "whom", "why", "why's", "with", "won't", "would", "wouldn't", "you", "you'd", "you'll", "you're", "you've", "your", "yours", "yourself", "yourselves"]); +window.stopwords = stopwords; // Export to global scope + + +/* Non-minified versions are copied as separate JavaScript files, if available */ +BaseStemmer=function(){this.current="",this.cursor=0,this.limit=0,this.limit_backward=0,this.bra=0,this.ket=0,this.setCurrent=function(t){this.current=t,this.cursor=0,this.limit=this.current.length,this.limit_backward=0,this.bra=this.cursor,this.ket=this.limit},this.getCurrent=function(){return this.current},this.copy_from=function(t){this.current=t.current,this.cursor=t.cursor,this.limit=t.limit,this.limit_backward=t.limit_backward,this.bra=t.bra,this.ket=t.ket},this.in_grouping=function(t,r,i){return!(this.cursor>=this.limit||i<(i=this.current.charCodeAt(this.cursor))||i>>3]&1<<(7&i))||(this.cursor++,0))},this.go_in_grouping=function(t,r,i){for(;this.cursor>>3]&1<<(7&s)))return!0;this.cursor++}return!1},this.in_grouping_b=function(t,r,i){return!(this.cursor<=this.limit_backward||i<(i=this.current.charCodeAt(this.cursor-1))||i>>3]&1<<(7&i))||(this.cursor--,0))},this.go_in_grouping_b=function(t,r,i){for(;this.cursor>this.limit_backward;){var s=this.current.charCodeAt(this.cursor-1);if(i>>3]&1<<(7&s)))return!0;this.cursor--}return!1},this.out_grouping=function(t,r,i){return!(this.cursor>=this.limit)&&(i<(i=this.current.charCodeAt(this.cursor))||i>>3]&1<<(7&i)))&&(this.cursor++,!0)},this.go_out_grouping=function(t,r,i){for(;this.cursor>>3]&1<<(7&s)))return!0;this.cursor++}return!1},this.out_grouping_b=function(t,r,i){return!(this.cursor<=this.limit_backward)&&(i<(i=this.current.charCodeAt(this.cursor-1))||i>>3]&1<<(7&i)))&&(this.cursor--,!0)},this.go_out_grouping_b=function(t,r,i){for(;this.cursor>this.limit_backward;){var s=this.current.charCodeAt(this.cursor-1);if(s<=i&&r<=s&&0!=(t[(s-=r)>>>3]&1<<(7&s)))return!0;this.cursor--}return!1},this.eq_s=function(t){return!(this.limit-this.cursor>>1),o=0,a=e=(l=t[r])[0].length){if(this.cursor=s+l[0].length,l.length<4)return l[2];var g=l[3](this);if(this.cursor=s+l[0].length,g)return l[2]}}while(0<=(r=l[1]));return 0},this.find_among_b=function(t){for(var r=0,i=t.length,s=this.cursor,h=this.limit_backward,e=0,n=0,c=!1;;){for(var u,o=r+(i-r>>1),a=0,l=e=(u=t[r])[0].length){if(this.cursor=s-u[0].length,u.length<4)return u[2];var g=u[3](this);if(this.cursor=s-u[0].length,g)return u[2]}}while(0<=(r=u[1]));return 0},this.replace_s=function(t,r,i){var s=i.length-(r-t);return this.current=this.current.slice(0,t)+i+this.current.slice(r),this.limit+=s,this.cursor>=r?this.cursor+=s:this.cursor>t&&(this.cursor=t),s},this.slice_check=function(){return!(this.bra<0||this.bra>this.ket||this.ket>this.limit||this.limit>this.current.length)},this.slice_from=function(t){var r=!1;return this.slice_check()&&(this.replace_s(this.bra,this.ket,t),r=!0),r},this.slice_del=function(){return this.slice_from("")},this.insert=function(t,r,i){r=this.replace_s(t,r,i);t<=this.bra&&(this.bra+=r),t<=this.ket&&(this.ket+=r)},this.slice_to=function(){var t="";return t=this.slice_check()?this.current.slice(this.bra,this.ket):t},this.assign_to=function(){return this.current.slice(0,this.limit)}}; +var EnglishStemmer=function(){var a=new BaseStemmer,c=[["arsen",-1,-1],["commun",-1,-1],["emerg",-1,-1],["gener",-1,-1],["later",-1,-1],["organ",-1,-1],["past",-1,-1],["univers",-1,-1]],o=[["'",-1,1],["'s'",0,1],["'s",-1,1]],u=[["ied",-1,2],["s",-1,3],["ies",1,2],["sses",1,1],["ss",1,-1],["us",1,-1]],t=[["succ",-1,1],["proc",-1,1],["exc",-1,1]],l=[["even",-1,2],["cann",-1,2],["inn",-1,2],["earr",-1,2],["herr",-1,2],["out",-1,2],["y",-1,1]],n=[["",-1,-1],["ed",0,2],["eed",1,1],["ing",0,3],["edly",0,2],["eedly",4,1],["ingly",0,2]],f=[["",-1,3],["bb",0,2],["dd",0,2],["ff",0,2],["gg",0,2],["bl",0,1],["mm",0,2],["nn",0,2],["pp",0,2],["rr",0,2],["at",0,1],["tt",0,2],["iz",0,1]],_=[["anci",-1,3],["enci",-1,2],["ogi",-1,14],["li",-1,16],["bli",3,12],["abli",4,4],["alli",3,8],["fulli",3,9],["lessli",3,15],["ousli",3,10],["entli",3,5],["aliti",-1,8],["biliti",-1,12],["iviti",-1,11],["tional",-1,1],["ational",14,7],["alism",-1,8],["ation",-1,7],["ization",17,6],["izer",-1,6],["ator",-1,7],["iveness",-1,11],["fulness",-1,9],["ousness",-1,10],["ogist",-1,13]],m=[["icate",-1,4],["ative",-1,6],["alize",-1,3],["iciti",-1,4],["ical",-1,4],["tional",-1,1],["ational",5,2],["ful",-1,5],["ness",-1,5]],b=[["ic",-1,1],["ance",-1,1],["ence",-1,1],["able",-1,1],["ible",-1,1],["ate",-1,1],["ive",-1,1],["ize",-1,1],["iti",-1,1],["al",-1,1],["ism",-1,1],["ion",-1,2],["er",-1,1],["ous",-1,1],["ant",-1,1],["ent",-1,1],["ment",15,1],["ement",16,1]],k=[["e",-1,1],["l",-1,2]],g=[["andes",-1,-1],["atlas",-1,-1],["bias",-1,-1],["cosmos",-1,-1],["early",-1,5],["gently",-1,3],["howe",-1,-1],["idly",-1,2],["news",-1,-1],["only",-1,6],["singly",-1,7],["skies",-1,1],["sky",-1,-1],["ugly",-1,4]],d=[17,64],v=[17,65,16,1],i=[1,17,65,208,1],w=[55,141,2],p=!1,y=0,h=0;function q(){var r=a.limit-a.cursor;return!!(a.out_grouping_b(i,89,121)&&a.in_grouping_b(v,97,121)&&a.out_grouping_b(v,97,121)||(a.cursor=a.limit-r,a.out_grouping_b(v,97,121)&&a.in_grouping_b(v,97,121)&&!(a.cursor>a.limit_backward))||(a.cursor=a.limit-r,a.eq_s_b("past")))}function z(){return h<=a.cursor}function Y(){return y<=a.cursor}this.stem=function(){var r=a.cursor;if(!(()=>{var r;if(a.bra=a.cursor,0!=(r=a.find_among(g))&&(a.ket=a.cursor,!(a.cursora.limit)a.cursor=i;else{a.cursor=e,a.cursor=r,(()=>{p=!1;var r=a.cursor;if(a.bra=a.cursor,!a.eq_s("'")||(a.ket=a.cursor,a.slice_del())){a.cursor=r;r=a.cursor;if(a.bra=a.cursor,a.eq_s("y")){if(a.ket=a.cursor,!a.slice_from("Y"))return;p=!0}a.cursor=r;for(r=a.cursor;;){var i=a.cursor;r:{for(;;){var e=a.cursor;if(a.in_grouping(v,97,121)&&(a.bra=a.cursor,a.eq_s("y"))){a.ket=a.cursor,a.cursor=e;break}if(a.cursor=e,a.cursor>=a.limit)break r;a.cursor++}if(!a.slice_from("Y"))return;p=!0;continue}a.cursor=i;break}a.cursor=r}})(),h=a.limit,y=a.limit;i=a.cursor;r:{var s=a.cursor;if(0==a.find_among(c)){if(a.cursor=s,!a.go_out_grouping(v,97,121))break r;if(a.cursor++,!a.go_in_grouping(v,97,121))break r;a.cursor++}h=a.cursor,a.go_out_grouping(v,97,121)&&(a.cursor++,a.go_in_grouping(v,97,121))&&(a.cursor++,y=a.cursor)}a.cursor=i,a.limit_backward=a.cursor,a.cursor=a.limit;var e=a.limit-a.cursor,r=((()=>{var r=a.limit-a.cursor;if(a.ket=a.cursor,0==a.find_among_b(o))a.cursor=a.limit-r;else if(a.bra=a.cursor,!a.slice_del())return;if(a.ket=a.cursor,0!=(r=a.find_among_b(u)))switch(a.bra=a.cursor,r){case 1:if(a.slice_from("ss"))break;return;case 2:r:{var i=a.limit-a.cursor,e=a.cursor-2;if(!(e{a.ket=a.cursor,o=a.find_among_b(n),a.bra=a.cursor;r:{var r=a.limit-a.cursor;i:{switch(o){case 1:var i=a.limit-a.cursor;e:{var e=a.limit-a.cursor;if(0==a.find_among_b(t)||a.cursor>a.limit_backward){if(a.cursor=a.limit-e,!z())break e;if(!a.slice_from("ee"))return}}a.cursor=a.limit-i;break;case 2:break i;case 3:if(0==(o=a.find_among_b(l)))break i;switch(o){case 1:var s=a.limit-a.cursor;if(!a.out_grouping_b(v,97,121))break i;if(a.cursor>a.limit_backward)break i;if(a.cursor=a.limit-s,a.bra=a.cursor,a.slice_from("ie"))break;return;case 2:if(a.cursor>a.limit_backward)break i}}break r}a.cursor=a.limit-r;var c=a.limit-a.cursor;if(!a.go_out_grouping_b(v,97,121))return;if(a.cursor--,a.cursor=a.limit-c,!a.slice_del())return;a.ket=a.cursor,a.bra=a.cursor;var o,c=a.limit-a.cursor;switch(o=a.find_among_b(f)){case 1:return a.slice_from("e");case 2:var u=a.limit-a.cursor;if(a.in_grouping_b(d,97,111)&&!(a.cursor>a.limit_backward))return;a.cursor=a.limit-u;break;case 3:return a.cursor!=h||(u=a.limit-a.cursor,q()&&(a.cursor=a.limit-u,a.slice_from("e")))}if(a.cursor=a.limit-c,a.ket=a.cursor,a.cursor<=a.limit_backward)return;if(a.cursor--,a.bra=a.cursor,!a.slice_del())return}})(),a.cursor=a.limit-r,a.limit-a.cursor),r=(a.ket=a.cursor,e=a.limit-a.cursor,(a.eq_s_b("y")||(a.cursor=a.limit-e,a.eq_s_b("Y")))&&(a.bra=a.cursor,a.out_grouping_b(v,97,121))&&a.cursor>a.limit_backward&&a.slice_from("i"),a.cursor=a.limit-i,a.limit-a.cursor),e=((()=>{var r;if(a.ket=a.cursor,0!=(r=a.find_among_b(_))&&(a.bra=a.cursor,z()))switch(r){case 1:if(a.slice_from("tion"))break;return;case 2:if(a.slice_from("ence"))break;return;case 3:if(a.slice_from("ance"))break;return;case 4:if(a.slice_from("able"))break;return;case 5:if(a.slice_from("ent"))break;return;case 6:if(a.slice_from("ize"))break;return;case 7:if(a.slice_from("ate"))break;return;case 8:if(a.slice_from("al"))break;return;case 9:if(a.slice_from("ful"))break;return;case 10:if(a.slice_from("ous"))break;return;case 11:if(a.slice_from("ive"))break;return;case 12:if(a.slice_from("ble"))break;return;case 13:if(a.slice_from("og"))break;return;case 14:if(!a.eq_s_b("l"))return;if(a.slice_from("og"))break;return;case 15:if(a.slice_from("less"))break;return;case 16:if(!a.in_grouping_b(w,99,116))return;if(a.slice_del())break}})(),a.cursor=a.limit-r,a.limit-a.cursor),i=((()=>{var r;if(a.ket=a.cursor,0!=(r=a.find_among_b(m))&&(a.bra=a.cursor,z()))switch(r){case 1:if(a.slice_from("tion"))break;return;case 2:if(a.slice_from("ate"))break;return;case 3:if(a.slice_from("al"))break;return;case 4:if(a.slice_from("ic"))break;return;case 5:if(a.slice_del())break;return;case 6:if(!Y())return;if(a.slice_del())break}})(),a.cursor=a.limit-e,a.limit-a.cursor),r=((()=>{var r;if(a.ket=a.cursor,0!=(r=a.find_among_b(b))&&(a.bra=a.cursor,Y()))switch(r){case 1:if(a.slice_del())break;return;case 2:var i=a.limit-a.cursor;if(!a.eq_s_b("s")&&(a.cursor=a.limit-i,!a.eq_s_b("t")))return;if(a.slice_del())break}})(),a.cursor=a.limit-i,a.limit-a.cursor),e=((()=>{var r;if(a.ket=a.cursor,0!=(r=a.find_among_b(k)))switch(a.bra=a.cursor,r){case 1:if(!Y()){if(!z())return;var i=a.limit-a.cursor;if(q())return;a.cursor=a.limit-i}if(a.slice_del())break;return;case 2:if(!Y())return;if(!a.eq_s_b("l"))return;if(a.slice_del())break}})(),a.cursor=a.limit-r,a.cursor=a.limit_backward,a.cursor);(()=>{if(p)for(;;){var r=a.cursor;r:{for(;;){var i=a.cursor;if(a.bra=a.cursor,a.eq_s("Y")){a.ket=a.cursor,a.cursor=i;break}if(a.cursor=i,a.cursor>=a.limit)break r;a.cursor++}if(a.slice_from("y"))continue;return}a.cursor=r;break}})(),a.cursor=e}}return!0},this.stemWord=function(r){return a.setCurrent(r),this.stem(),a.getCurrent()}}; +window.Stemmer = EnglishStemmer; diff --git a/docs_local/_build/_static/minus.png b/docs_local/_build/_static/minus.png new file mode 100644 index 0000000000000000000000000000000000000000..d96755fdaf8bb2214971e0db9c1fd3077d7c419d GIT binary patch literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^+#t*WBp7;*Yy1LIik>cxAr*|t7R?Mi>2?kWtu=nj kDsEF_5m^0CR;1wuP-*O&G^0G}KYk!hp00i_>zopr08q^qX#fBK literal 0 HcmV?d00001 diff --git a/docs_local/_build/_static/plus.png b/docs_local/_build/_static/plus.png new file mode 100644 index 0000000000000000000000000000000000000000..7107cec93a979b9a5f64843235a16651d563ce2d GIT binary patch literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^+#t*WBp7;*Yy1LIik>cxAr*|t7R?Mi>2?kWtu>-2 m3q%Vub%g%s<8sJhVPMczOq}xhg9DJoz~JfX=d#Wzp$Pyb1r*Kz literal 0 HcmV?d00001 diff --git a/docs_local/_build/_static/pygments.css b/docs_local/_build/_static/pygments.css new file mode 100644 index 00000000..9392ddcb --- /dev/null +++ b/docs_local/_build/_static/pygments.css @@ -0,0 +1,84 @@ +pre { line-height: 125%; } +td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +.highlight .hll { background-color: #ffffcc } +.highlight { background: #f8f8f8; } +.highlight .c { color: #8F5902; font-style: italic } /* Comment */ +.highlight .err { color: #A40000; border: 1px solid #EF2929 } /* Error */ +.highlight .g { color: #000 } /* Generic */ +.highlight .k { color: #004461; font-weight: bold } /* Keyword */ +.highlight .l { color: #000 } /* Literal */ +.highlight .n { color: #000 } /* Name */ +.highlight .o { color: #582800 } /* Operator */ +.highlight .x { color: #000 } /* Other */ +.highlight .p { color: #000; font-weight: bold } /* Punctuation */ +.highlight .ch { color: #8F5902; font-style: italic } /* Comment.Hashbang */ +.highlight .cm { color: #8F5902; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #8F5902 } /* Comment.Preproc */ +.highlight .cpf { color: #8F5902; font-style: italic } /* Comment.PreprocFile */ +.highlight .c1 { color: #8F5902; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #8F5902; font-style: italic } /* Comment.Special */ +.highlight .gd { color: #A40000 } /* Generic.Deleted */ +.highlight .ge { color: #000; font-style: italic } /* Generic.Emph */ +.highlight .ges { color: #000 } /* Generic.EmphStrong */ +.highlight .gr { color: #EF2929 } /* Generic.Error */ +.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.highlight .gi { color: #00A000 } /* Generic.Inserted */ +.highlight .go { color: #888 } /* Generic.Output */ +.highlight .gp { color: #745334 } /* Generic.Prompt */ +.highlight .gs { color: #000; font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.highlight .gt { color: #A40000; font-weight: bold } /* Generic.Traceback */ +.highlight .kc { color: #004461; font-weight: bold } /* Keyword.Constant */ +.highlight .kd { color: #004461; font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { color: #004461; font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { color: #004461; font-weight: bold } /* Keyword.Pseudo */ +.highlight .kr { color: #004461; font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #004461; font-weight: bold } /* Keyword.Type */ +.highlight .ld { color: #000 } /* Literal.Date */ +.highlight .m { color: #900 } /* Literal.Number */ +.highlight .s { color: #4E9A06 } /* Literal.String */ +.highlight .na { color: #C4A000 } /* Name.Attribute */ +.highlight .nb { color: #004461 } /* Name.Builtin */ +.highlight .nc { color: #000 } /* Name.Class */ +.highlight .no { color: #000 } /* Name.Constant */ +.highlight .nd { color: #888 } /* Name.Decorator */ +.highlight .ni { color: #CE5C00 } /* Name.Entity */ +.highlight .ne { color: #C00; font-weight: bold } /* Name.Exception */ +.highlight .nf { color: #000 } /* Name.Function */ +.highlight .nl { color: #F57900 } /* Name.Label */ +.highlight .nn { color: #000 } /* Name.Namespace */ +.highlight .nx { color: #000 } /* Name.Other */ +.highlight .py { color: #000 } /* Name.Property */ +.highlight .nt { color: #004461; font-weight: bold } /* Name.Tag */ +.highlight .nv { color: #000 } /* Name.Variable */ +.highlight .ow { color: #004461; font-weight: bold } /* Operator.Word */ +.highlight .pm { color: #000; font-weight: bold } /* Punctuation.Marker */ +.highlight .w { color: #F8F8F8 } /* Text.Whitespace */ +.highlight .mb { color: #900 } /* Literal.Number.Bin */ +.highlight .mf { color: #900 } /* Literal.Number.Float */ +.highlight .mh { color: #900 } /* Literal.Number.Hex */ +.highlight .mi { color: #900 } /* Literal.Number.Integer */ +.highlight .mo { color: #900 } /* Literal.Number.Oct */ +.highlight .sa { color: #4E9A06 } /* Literal.String.Affix */ +.highlight .sb { color: #4E9A06 } /* Literal.String.Backtick */ +.highlight .sc { color: #4E9A06 } /* Literal.String.Char */ +.highlight .dl { color: #4E9A06 } /* Literal.String.Delimiter */ +.highlight .sd { color: #8F5902; font-style: italic } /* Literal.String.Doc */ +.highlight .s2 { color: #4E9A06 } /* Literal.String.Double */ +.highlight .se { color: #4E9A06 } /* Literal.String.Escape */ +.highlight .sh { color: #4E9A06 } /* Literal.String.Heredoc */ +.highlight .si { color: #4E9A06 } /* Literal.String.Interpol */ +.highlight .sx { color: #4E9A06 } /* Literal.String.Other */ +.highlight .sr { color: #4E9A06 } /* Literal.String.Regex */ +.highlight .s1 { color: #4E9A06 } /* Literal.String.Single */ +.highlight .ss { color: #4E9A06 } /* Literal.String.Symbol */ +.highlight .bp { color: #3465A4 } /* Name.Builtin.Pseudo */ +.highlight .fm { color: #000 } /* Name.Function.Magic */ +.highlight .vc { color: #000 } /* Name.Variable.Class */ +.highlight .vg { color: #000 } /* Name.Variable.Global */ +.highlight .vi { color: #000 } /* Name.Variable.Instance */ +.highlight .vm { color: #000 } /* Name.Variable.Magic */ +.highlight .il { color: #900 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/docs_local/_build/_static/searchtools.js b/docs_local/_build/_static/searchtools.js new file mode 100644 index 00000000..e29b1c75 --- /dev/null +++ b/docs_local/_build/_static/searchtools.js @@ -0,0 +1,693 @@ +/* + * Sphinx JavaScript utilities for the full-text search. + */ +"use strict"; + +/** + * Simple result scoring code. + */ +if (typeof Scorer === "undefined") { + var Scorer = { + // Implement the following function to further tweak the score for each result + // The function takes a result array [docname, title, anchor, descr, score, filename] + // and returns the new score. + /* + score: result => { + const [docname, title, anchor, descr, score, filename, kind] = result + return score + }, + */ + + // query matches the full name of an object + objNameMatch: 11, + // or matches in the last dotted part of the object name + objPartialMatch: 6, + // Additive scores depending on the priority of the object + objPrio: { + 0: 15, // used to be importantResults + 1: 5, // used to be objectResults + 2: -5, // used to be unimportantResults + }, + // Used when the priority is not in the mapping. + objPrioDefault: 0, + + // query found in title + title: 15, + partialTitle: 7, + // query found in terms + term: 5, + partialTerm: 2, + }; +} + +// Global search result kind enum, used by themes to style search results. +// prettier-ignore +class SearchResultKind { + static get index() { return "index"; } + static get object() { return "object"; } + static get text() { return "text"; } + static get title() { return "title"; } +} + +const _removeChildren = (element) => { + while (element && element.lastChild) element.removeChild(element.lastChild); +}; + +/** + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping + */ +const _escapeRegExp = (string) => + string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string + +const _escapeHTML = (text) => { + return text + .replaceAll("&", "&") + .replaceAll("<", "<") + .replaceAll(">", ">") + .replaceAll('"', """) + .replaceAll("'", "'"); +}; + +const _displayItem = (item, searchTerms, highlightTerms) => { + const docBuilder = DOCUMENTATION_OPTIONS.BUILDER; + const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX; + const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX; + const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY; + const contentRoot = document.documentElement.dataset.content_root; + + const [docName, title, anchor, descr, score, _filename, kind] = item; + + let listItem = document.createElement("li"); + // Add a class representing the item's type: + // can be used by a theme's CSS selector for styling + // See SearchResultKind for the class names. + listItem.classList.add(`kind-${kind}`); + let requestUrl; + let linkUrl; + if (docBuilder === "dirhtml") { + // dirhtml builder + let dirname = docName + "/"; + if (dirname.match(/\/index\/$/)) + dirname = dirname.substring(0, dirname.length - 6); + else if (dirname === "index/") dirname = ""; + requestUrl = contentRoot + dirname; + linkUrl = requestUrl; + } else { + // normal html builders + requestUrl = contentRoot + docName + docFileSuffix; + linkUrl = docName + docLinkSuffix; + } + let linkEl = listItem.appendChild(document.createElement("a")); + linkEl.href = linkUrl + anchor; + linkEl.dataset.score = score; + linkEl.innerHTML = _escapeHTML(title); + if (descr) { + listItem.appendChild(document.createElement("span")).innerHTML = + ` (${_escapeHTML(descr)})`; + // highlight search terms in the description + if (SPHINX_HIGHLIGHT_ENABLED) + // SPHINX_HIGHLIGHT_ENABLED is set in sphinx_highlight.js + highlightTerms.forEach((term) => + _highlightText(listItem, term, "highlighted"), + ); + } else if (showSearchSummary) + fetch(requestUrl) + .then((responseData) => responseData.text()) + .then((data) => { + if (data) + listItem.appendChild( + Search.makeSearchSummary(data, searchTerms, anchor), + ); + // highlight search terms in the summary + if (SPHINX_HIGHLIGHT_ENABLED) + // SPHINX_HIGHLIGHT_ENABLED is set in sphinx_highlight.js + highlightTerms.forEach((term) => + _highlightText(listItem, term, "highlighted"), + ); + }); + Search.output.appendChild(listItem); +}; +const _finishSearch = (resultCount) => { + Search.stopPulse(); + Search.title.innerText = _("Search Results"); + if (!resultCount) + Search.status.innerText = Documentation.gettext( + "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.", + ); + else + Search.status.innerText = Documentation.ngettext( + "Search finished, found one page matching the search query.", + "Search finished, found ${resultCount} pages matching the search query.", + resultCount, + ).replace("${resultCount}", resultCount); +}; +const _displayNextItem = ( + results, + resultCount, + searchTerms, + highlightTerms, +) => { + // results left, load the summary and display it + // this is intended to be dynamic (don't sub resultsCount) + if (results.length) { + _displayItem(results.pop(), searchTerms, highlightTerms); + setTimeout( + () => _displayNextItem(results, resultCount, searchTerms, highlightTerms), + 5, + ); + } + // search finished, update title and status message + else _finishSearch(resultCount); +}; +// Helper function used by query() to order search results. +// Each input is an array of [docname, title, anchor, descr, score, filename, kind]. +// Order the results by score (in opposite order of appearance, since the +// `_displayNextItem` function uses pop() to retrieve items) and then alphabetically. +const _orderResultsByScoreThenName = (a, b) => { + const leftScore = a[4]; + const rightScore = b[4]; + if (leftScore === rightScore) { + // same score: sort alphabetically + const leftTitle = a[1].toLowerCase(); + const rightTitle = b[1].toLowerCase(); + if (leftTitle === rightTitle) return 0; + return leftTitle > rightTitle ? -1 : 1; // inverted is intentional + } + return leftScore > rightScore ? 1 : -1; +}; + +/** + * Default splitQuery function. Can be overridden in ``sphinx.search`` with a + * custom function per language. + * + * The regular expression works by splitting the string on consecutive characters + * that are not Unicode letters, numbers, underscores, or emoji characters. + * This is the same as ``\W+`` in Python, preserving the surrogate pair area. + */ +if (typeof splitQuery === "undefined") { + var splitQuery = (query) => + query + .split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu) + .filter((term) => term); // remove remaining empty strings +} + +/** + * Search Module + */ +const Search = { + _index: null, + _queued_query: null, + _pulse_status: -1, + + htmlToText: (htmlString, anchor) => { + const htmlElement = new DOMParser().parseFromString( + htmlString, + "text/html", + ); + for (const removalQuery of [".headerlink", "script", "style"]) { + htmlElement.querySelectorAll(removalQuery).forEach((el) => { + el.remove(); + }); + } + if (anchor) { + const anchorContent = htmlElement.querySelector( + `[role="main"] ${anchor}`, + ); + if (anchorContent) return anchorContent.textContent; + + console.warn( + `Anchored content block not found. Sphinx search tries to obtain it via DOM query '[role=main] ${anchor}'. Check your theme or template.`, + ); + } + + // if anchor not specified or not found, fall back to main content + const docContent = htmlElement.querySelector('[role="main"]'); + if (docContent) return docContent.textContent; + + console.warn( + "Content block not found. Sphinx search tries to obtain it via DOM query '[role=main]'. Check your theme or template.", + ); + return ""; + }, + + init: () => { + const query = new URLSearchParams(window.location.search).get("q"); + document + .querySelectorAll('input[name="q"]') + .forEach((el) => (el.value = query)); + if (query) Search.performSearch(query); + }, + + loadIndex: (url) => + (document.body.appendChild(document.createElement("script")).src = url), + + setIndex: (index) => { + Search._index = index; + if (Search._queued_query !== null) { + const query = Search._queued_query; + Search._queued_query = null; + Search.query(query); + } + }, + + hasIndex: () => Search._index !== null, + + deferQuery: (query) => (Search._queued_query = query), + + stopPulse: () => (Search._pulse_status = -1), + + startPulse: () => { + if (Search._pulse_status >= 0) return; + + const pulse = () => { + Search._pulse_status = (Search._pulse_status + 1) % 4; + Search.dots.innerText = ".".repeat(Search._pulse_status); + if (Search._pulse_status >= 0) window.setTimeout(pulse, 500); + }; + pulse(); + }, + + /** + * perform a search for something (or wait until index is loaded) + */ + performSearch: (query) => { + // create the required interface elements + const searchText = document.createElement("h2"); + searchText.textContent = _("Searching"); + const searchSummary = document.createElement("p"); + searchSummary.classList.add("search-summary"); + searchSummary.innerText = ""; + const searchList = document.createElement("ul"); + searchList.setAttribute("role", "list"); + searchList.classList.add("search"); + + const out = document.getElementById("search-results"); + Search.title = out.appendChild(searchText); + Search.dots = Search.title.appendChild(document.createElement("span")); + Search.status = out.appendChild(searchSummary); + Search.output = out.appendChild(searchList); + + const searchProgress = document.getElementById("search-progress"); + // Some themes don't use the search progress node + if (searchProgress) { + searchProgress.innerText = _("Preparing search..."); + } + Search.startPulse(); + + // index already loaded, the browser was quick! + if (Search.hasIndex()) Search.query(query); + else Search.deferQuery(query); + }, + + _parseQuery: (query) => { + // stem the search terms and add them to the correct list + const stemmer = new Stemmer(); + const searchTerms = new Set(); + const excludedTerms = new Set(); + const highlightTerms = new Set(); + const objectTerms = new Set(splitQuery(query.toLowerCase().trim())); + splitQuery(query.trim()).forEach((queryTerm) => { + const queryTermLower = queryTerm.toLowerCase(); + + // maybe skip this "word" + // stopwords set is from language_data.js + if (stopwords.has(queryTermLower) || queryTerm.match(/^\d+$/)) return; + + // stem the word + let word = stemmer.stemWord(queryTermLower); + // select the correct list + if (word[0] === "-") excludedTerms.add(word.substr(1)); + else { + searchTerms.add(word); + highlightTerms.add(queryTermLower); + } + }); + + if (SPHINX_HIGHLIGHT_ENABLED) { + // SPHINX_HIGHLIGHT_ENABLED is set in sphinx_highlight.js + localStorage.setItem( + "sphinx_highlight_terms", + [...highlightTerms].join(" "), + ); + } + + // console.debug("SEARCH: searching for:"); + // console.info("required: ", [...searchTerms]); + // console.info("excluded: ", [...excludedTerms]); + + return [query, searchTerms, excludedTerms, highlightTerms, objectTerms]; + }, + + /** + * execute search (requires search index to be loaded) + */ + _performSearch: ( + query, + searchTerms, + excludedTerms, + highlightTerms, + objectTerms, + ) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + const allTitles = Search._index.alltitles; + const indexEntries = Search._index.indexentries; + + // Collect multiple result groups to be sorted separately and then ordered. + // Each is an array of [docname, title, anchor, descr, score, filename, kind]. + const normalResults = []; + const nonMainIndexResults = []; + + _removeChildren(document.getElementById("search-progress")); + + const queryLower = query.toLowerCase().trim(); + for (const [title, foundTitles] of Object.entries(allTitles)) { + if ( + title.toLowerCase().trim().includes(queryLower) + && queryLower.length >= title.length / 2 + ) { + for (const [file, id] of foundTitles) { + const score = Math.round( + (Scorer.title * queryLower.length) / title.length, + ); + const boost = titles[file] === title ? 1 : 0; // add a boost for document titles + normalResults.push([ + docNames[file], + titles[file] !== title ? `${titles[file]} > ${title}` : title, + id !== null ? "#" + id : "", + null, + score + boost, + filenames[file], + SearchResultKind.title, + ]); + } + } + } + + // search for explicit entries in index directives + for (const [entry, foundEntries] of Object.entries(indexEntries)) { + if (entry.includes(queryLower) && queryLower.length >= entry.length / 2) { + for (const [file, id, isMain] of foundEntries) { + const score = Math.round((100 * queryLower.length) / entry.length); + const result = [ + docNames[file], + titles[file], + id ? "#" + id : "", + null, + score, + filenames[file], + SearchResultKind.index, + ]; + if (isMain) { + normalResults.push(result); + } else { + nonMainIndexResults.push(result); + } + } + } + } + + // lookup as object + objectTerms.forEach((term) => + normalResults.push(...Search.performObjectSearch(term, objectTerms)), + ); + + // lookup as search terms in fulltext + normalResults.push( + ...Search.performTermsSearch(searchTerms, excludedTerms), + ); + + // let the scorer override scores with a custom scoring function + if (Scorer.score) { + normalResults.forEach((item) => (item[4] = Scorer.score(item))); + nonMainIndexResults.forEach((item) => (item[4] = Scorer.score(item))); + } + + // Sort each group of results by score and then alphabetically by name. + normalResults.sort(_orderResultsByScoreThenName); + nonMainIndexResults.sort(_orderResultsByScoreThenName); + + // Combine the result groups in (reverse) order. + // Non-main index entries are typically arbitrary cross-references, + // so display them after other results. + let results = [...nonMainIndexResults, ...normalResults]; + + // remove duplicate search results + // note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept + let seen = new Set(); + results = results.reverse().reduce((acc, result) => { + let resultStr = result + .slice(0, 4) + .concat([result[5]]) + .map((v) => String(v)) + .join(","); + if (!seen.has(resultStr)) { + acc.push(result); + seen.add(resultStr); + } + return acc; + }, []); + + return results.reverse(); + }, + + query: (query) => { + const [ + searchQuery, + searchTerms, + excludedTerms, + highlightTerms, + objectTerms, + ] = Search._parseQuery(query); + const results = Search._performSearch( + searchQuery, + searchTerms, + excludedTerms, + highlightTerms, + objectTerms, + ); + + // for debugging + //Search.lastresults = results.slice(); // a copy + // console.info("search results:", Search.lastresults); + + // print the results + _displayNextItem(results, results.length, searchTerms, highlightTerms); + }, + + /** + * search for object names + */ + performObjectSearch: (object, objectTerms) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const objects = Search._index.objects; + const objNames = Search._index.objnames; + const titles = Search._index.titles; + + const results = []; + + const objectSearchCallback = (prefix, match) => { + const name = match[4]; + const fullname = (prefix ? prefix + "." : "") + name; + const fullnameLower = fullname.toLowerCase(); + if (fullnameLower.indexOf(object) < 0) return; + + let score = 0; + const parts = fullnameLower.split("."); + + // check for different match types: exact matches of full name or + // "last name" (i.e. last dotted part) + if (fullnameLower === object || parts.slice(-1)[0] === object) + score += Scorer.objNameMatch; + else if (parts.slice(-1)[0].indexOf(object) > -1) + score += Scorer.objPartialMatch; // matches in last name + + const objName = objNames[match[1]][2]; + const title = titles[match[0]]; + + // If more than one term searched for, we require other words to be + // found in the name/title/description + const otherTerms = new Set(objectTerms); + otherTerms.delete(object); + if (otherTerms.size > 0) { + const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase(); + if ( + [...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0) + ) + return; + } + + let anchor = match[3]; + if (anchor === "") anchor = fullname; + else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname; + + const descr = objName + _(", in ") + title; + + // add custom score for some objects according to scorer + if (Scorer.objPrio.hasOwnProperty(match[2])) + score += Scorer.objPrio[match[2]]; + else score += Scorer.objPrioDefault; + + results.push([ + docNames[match[0]], + fullname, + "#" + anchor, + descr, + score, + filenames[match[0]], + SearchResultKind.object, + ]); + }; + Object.keys(objects).forEach((prefix) => + objects[prefix].forEach((array) => objectSearchCallback(prefix, array)), + ); + return results; + }, + + /** + * search for full-text terms in the index + */ + performTermsSearch: (searchTerms, excludedTerms) => { + // prepare search + const terms = Search._index.terms; + const titleTerms = Search._index.titleterms; + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + + const scoreMap = new Map(); + const fileMap = new Map(); + + // perform the search on the required terms + searchTerms.forEach((word) => { + const files = []; + // find documents, if any, containing the query word in their text/title term indices + // use Object.hasOwnProperty to avoid mismatching against prototype properties + const arr = [ + { + files: terms.hasOwnProperty(word) ? terms[word] : undefined, + score: Scorer.term, + }, + { + files: titleTerms.hasOwnProperty(word) ? titleTerms[word] : undefined, + score: Scorer.title, + }, + ]; + // add support for partial matches + if (word.length > 2) { + const escapedWord = _escapeRegExp(word); + if (!terms.hasOwnProperty(word)) { + Object.keys(terms).forEach((term) => { + if (term.match(escapedWord)) + arr.push({ files: terms[term], score: Scorer.partialTerm }); + }); + } + if (!titleTerms.hasOwnProperty(word)) { + Object.keys(titleTerms).forEach((term) => { + if (term.match(escapedWord)) + arr.push({ files: titleTerms[term], score: Scorer.partialTitle }); + }); + } + } + + // no match but word was a required one + if (arr.every((record) => record.files === undefined)) return; + + // found search word in contents + arr.forEach((record) => { + if (record.files === undefined) return; + + let recordFiles = record.files; + if (recordFiles.length === undefined) recordFiles = [recordFiles]; + files.push(...recordFiles); + + // set score for the word in each file + recordFiles.forEach((file) => { + if (!scoreMap.has(file)) scoreMap.set(file, new Map()); + const fileScores = scoreMap.get(file); + fileScores.set(word, record.score); + }); + }); + + // create the mapping + files.forEach((file) => { + if (!fileMap.has(file)) fileMap.set(file, [word]); + else if (fileMap.get(file).indexOf(word) === -1) + fileMap.get(file).push(word); + }); + }); + + // now check if the files don't contain excluded terms + const results = []; + for (const [file, wordList] of fileMap) { + // check if all requirements are matched + + // as search terms with length < 3 are discarded + const filteredTermCount = [...searchTerms].filter( + (term) => term.length > 2, + ).length; + if ( + wordList.length !== searchTerms.size + && wordList.length !== filteredTermCount + ) + continue; + + // ensure that none of the excluded terms is in the search result + if ( + [...excludedTerms].some( + (term) => + terms[term] === file + || titleTerms[term] === file + || (terms[term] || []).includes(file) + || (titleTerms[term] || []).includes(file), + ) + ) + break; + + // select one (max) score for the file. + const score = Math.max(...wordList.map((w) => scoreMap.get(file).get(w))); + // add result to the result list + results.push([ + docNames[file], + titles[file], + "", + null, + score, + filenames[file], + SearchResultKind.text, + ]); + } + return results; + }, + + /** + * helper function to return a node containing the + * search summary for a given text. keywords is a list + * of stemmed words. + */ + makeSearchSummary: (htmlText, keywords, anchor) => { + const text = Search.htmlToText(htmlText, anchor); + if (text === "") return null; + + const textLower = text.toLowerCase(); + const actualStartPosition = [...keywords] + .map((k) => textLower.indexOf(k.toLowerCase())) + .filter((i) => i > -1) + .slice(-1)[0]; + const startWithContext = Math.max(actualStartPosition - 120, 0); + + const top = startWithContext === 0 ? "" : "..."; + const tail = startWithContext + 240 < text.length ? "..." : ""; + + let summary = document.createElement("p"); + summary.classList.add("context"); + summary.textContent = + top + text.substr(startWithContext, 240).trim() + tail; + + return summary; + }, +}; + +_ready(Search.init); diff --git a/docs_local/_build/_static/sphinx_highlight.js b/docs_local/_build/_static/sphinx_highlight.js new file mode 100644 index 00000000..a74e103a --- /dev/null +++ b/docs_local/_build/_static/sphinx_highlight.js @@ -0,0 +1,159 @@ +/* Highlighting utilities for Sphinx HTML documentation. */ +"use strict"; + +const SPHINX_HIGHLIGHT_ENABLED = true; + +/** + * highlight a given string on a node by wrapping it in + * span elements with the given class name. + */ +const _highlight = (node, addItems, text, className) => { + if (node.nodeType === Node.TEXT_NODE) { + const val = node.nodeValue; + const parent = node.parentNode; + const pos = val.toLowerCase().indexOf(text); + if ( + pos >= 0 + && !parent.classList.contains(className) + && !parent.classList.contains("nohighlight") + ) { + let span; + + const closestNode = parent.closest("body, svg, foreignObject"); + const isInSVG = closestNode && closestNode.matches("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.classList.add(className); + } + + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + const rest = document.createTextNode(val.substr(pos + text.length)); + parent.insertBefore(span, parent.insertBefore(rest, node.nextSibling)); + node.nodeValue = val.substr(0, pos); + /* There may be more occurrences of search term in this node. So call this + * function recursively on the remaining fragment. + */ + _highlight(rest, addItems, text, className); + + if (isInSVG) { + const rect = document.createElementNS( + "http://www.w3.org/2000/svg", + "rect", + ); + const bbox = parent.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute("class", className); + addItems.push({ parent: parent, target: rect }); + } + } + } else if (node.matches && !node.matches("button, select, textarea")) { + node.childNodes.forEach((el) => _highlight(el, addItems, text, className)); + } +}; +const _highlightText = (thisNode, text, className) => { + let addItems = []; + _highlight(thisNode, addItems, text, className); + addItems.forEach((obj) => + obj.parent.insertAdjacentElement("beforebegin", obj.target), + ); +}; + +/** + * Small JavaScript module for the documentation. + */ +const SphinxHighlight = { + /** + * highlight the search words provided in localstorage in the text + */ + highlightSearchWords: () => { + if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight + + // get and clear terms from localstorage + const url = new URL(window.location); + const highlight = + localStorage.getItem("sphinx_highlight_terms") + || url.searchParams.get("highlight") + || ""; + localStorage.removeItem("sphinx_highlight_terms"); + // Update history only if '?highlight' is present; otherwise it + // clears text fragments (not set in window.location by the browser) + if (url.searchParams.has("highlight")) { + url.searchParams.delete("highlight"); + window.history.replaceState({}, "", url); + } + + // get individual terms from highlight string + const terms = highlight + .toLowerCase() + .split(/\s+/) + .filter((x) => x); + if (terms.length === 0) return; // nothing to do + + // There should never be more than one element matching "div.body" + const divBody = document.querySelectorAll("div.body"); + const body = divBody.length ? divBody[0] : document.querySelector("body"); + window.setTimeout(() => { + terms.forEach((term) => _highlightText(body, term, "highlighted")); + }, 10); + + const searchBox = document.getElementById("searchbox"); + if (searchBox === null) return; + searchBox.appendChild( + document + .createRange() + .createContextualFragment( + '

", + ), + ); + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords: () => { + document + .querySelectorAll("#searchbox .highlight-link") + .forEach((el) => el.remove()); + document + .querySelectorAll("span.highlighted") + .forEach((el) => el.classList.remove("highlighted")); + localStorage.removeItem("sphinx_highlight_terms"); + }, + + initEscapeListener: () => { + // only install a listener if it is really needed + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) + return; + // bail with special keys + if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) + return; + if ( + DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS + && event.key === "Escape" + ) { + SphinxHighlight.hideSearchWords(); + event.preventDefault(); + } + }); + }, +}; + +_ready(() => { + /* Do not call highlightSearchWords() when we are on the search page. + * It will highlight words from the *previous* search query. + */ + if (typeof Search === "undefined") SphinxHighlight.highlightSearchWords(); + SphinxHighlight.initEscapeListener(); +}); diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/claude_skill/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/claude_skill/index.html new file mode 100644 index 00000000..46d28d74 --- /dev/null +++ b/docs_local/_build/autoapi/PowerPlatform/Dataverse/claude_skill/index.html @@ -0,0 +1,122 @@ + + + + + + + + PowerPlatform.Dataverse.claude_skill — PowerPlatform-Dataverse-Client 0.1.0b11 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

PowerPlatform.Dataverse.claude_skill

+

Claude Code skill package for the PowerPlatform Dataverse Client SDK.

+

This package contains two skills: +- dataverse-sdk-use: Guidance for using the SDK in your applications +- dataverse-sdk-dev: Guidance for developing/contributing to the SDK itself

+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/client/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/client/index.html new file mode 100644 index 00000000..130299b2 --- /dev/null +++ b/docs_local/_build/autoapi/PowerPlatform/Dataverse/client/index.html @@ -0,0 +1,295 @@ + + + + + + + + PowerPlatform.Dataverse.client — PowerPlatform-Dataverse-Client 0.1.0b11 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

PowerPlatform.Dataverse.client

+
+

Classes

+ + + + + + +

DataverseClient

High-level client for Microsoft Dataverse operations.

+
+
+

Module Contents

+
+
+class PowerPlatform.Dataverse.client.DataverseClient(base_url: str, credential: azure.core.credentials.TokenCredential, config: PowerPlatform.Dataverse.core.config.DataverseConfig | None = None, *, context: PowerPlatform.Dataverse.core.config.OperationContext | None = None)
+

High-level client for Microsoft Dataverse operations.

+

This client provides a simple, stable interface for interacting with Dataverse environments +through the Web API. It handles authentication via Azure Identity and delegates HTTP operations +to an internal OData client.

+
+
Key capabilities:
    +
  • OData CRUD operations: create, read, update, delete records

  • +
  • SQL queries: execute read-only SQL via Web API ?sql parameter

  • +
  • Table metadata: create, inspect, and delete custom tables; create and delete columns

  • +
  • File uploads: upload files to file columns with chunking support

  • +
+
+
+
+
Parameters:
+
    +
  • base_url (str) – Your Dataverse environment URL, for example +"https://org.crm.dynamics.com". Trailing slash is automatically removed.

  • +
  • credential (TokenCredential) – Azure Identity credential for authentication.

  • +
  • config (DataverseConfig or None) – Optional configuration for language, timeouts, and retries. +If not provided, defaults are loaded from from_env().

  • +
  • context (OperationContext or None) – Optional caller-defined context object appended to the +outbound User-Agent header for plugin/tool attribution. Cannot be used +together with config – pass the context via +DataverseConfig instead.

  • +
+
+
Raises:
+
    +
  • ValueError – If base_url is missing or empty after trimming.

  • +
  • ValueError – If both config and context are provided.

  • +
+
+
+
+

Note

+

The client lazily initializes its internal OData client on first use, allowing lightweight construction without immediate network calls.

+
+
+

Note

+

All methods that communicate with the Dataverse Web API may raise +HttpError on non-successful +HTTP responses (e.g. 401, 403, 404, 429, 500). Individual method +docstrings document only domain-specific exceptions.

+
+

Operations are organized into namespaces:

+
    +
  • client.records – create, update, delete, and get records (single or paginated queries)

  • +
  • client.query – query and search operations

  • +
  • client.tables – table and column metadata management

  • +
  • client.files – file upload operations

  • +
  • client.dataframe – pandas DataFrame wrappers for record CRUD

  • +
  • client.batch – batch multiple operations into a single HTTP request

  • +
+

The client supports Python’s context manager protocol for automatic resource +cleanup and HTTP connection pooling:

+

Example

+

Recommended – context manager (enables HTTP connection pooling):

+
from azure.identity import InteractiveBrowserCredential
+from PowerPlatform.Dataverse.client import DataverseClient
+
+credential = InteractiveBrowserCredential()
+
+with DataverseClient("https://org.crm.dynamics.com", credential) as client:
+    record_id = client.records.create("account", {"name": "Contoso Ltd"})
+    client.records.update("account", record_id, {"telephone1": "555-0100"})
+# Session closed, caches cleared automatically
+
+
+

Manual lifecycle:

+
client = DataverseClient("https://org.crm.dynamics.com", credential)
+try:
+    record_id = client.records.create("account", {"name": "Contoso Ltd"})
+finally:
+    client.close()
+
+
+
+
+auth
+
+ +
+
+records
+
+ +
+
+query
+
+ +
+
+tables
+
+ +
+
+files
+
+ +
+
+dataframe
+
+ +
+
+batch
+
+ +
+
+close() None
+

Close the client and release resources.

+

Closes the HTTP session (if any), clears internal caches, and +marks the client as closed. Safe to call multiple times. After +closing, any operation will raise RuntimeError.

+

Called automatically when using the client as a context manager.

+

Example:

+
client = DataverseClient(base_url, credential)
+try:
+    client.records.create("account", {"name": "Contoso"})
+finally:
+    client.close()
+
+
+
+ +
+
+flush_cache(kind) int
+

Flush cached client metadata or state.

+
+
Parameters:
+

kind (str) –

Cache kind to flush. Currently supported values:

+
    +
  • "picklist": Clears picklist label cache used for label-to-integer conversion

  • +
+

Future kinds (e.g. "entityset", "primaryid") may be added without +breaking this signature.

+

+
+
Returns:
+

Number of cache entries removed.

+
+
Return type:
+

int

+
+
+

Example

+

Clear the picklist cache:

+
removed = client.flush_cache("picklist")
+print(f"Cleared {removed} cached picklist entries")
+
+
+
+ +
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/common/constants/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/common/constants/index.html new file mode 100644 index 00000000..2d696a88 --- /dev/null +++ b/docs_local/_build/autoapi/PowerPlatform/Dataverse/common/constants/index.html @@ -0,0 +1,209 @@ + + + + + + + + PowerPlatform.Dataverse.common.constants — PowerPlatform-Dataverse-Client 0.1.0b11 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

PowerPlatform.Dataverse.common.constants

+

Constants for Dataverse Web API metadata types.

+

These constants define the OData type identifiers used in Web API payloads +for metadata operations.

+
+

Attributes

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

ODATA_TYPE_LOCALIZED_LABEL

ODATA_TYPE_LABEL

ODATA_TYPE_LOOKUP_ATTRIBUTE

ODATA_TYPE_ONE_TO_MANY_RELATIONSHIP

ODATA_TYPE_MANY_TO_MANY_RELATIONSHIP

CASCADE_BEHAVIOR_CASCADE

Perform the action on all referencing table records associated with the referenced table record.

CASCADE_BEHAVIOR_NO_CASCADE

Do not apply the action to any referencing table records associated with the referenced table record.

CASCADE_BEHAVIOR_REMOVE_LINK

Remove the value of the referencing column for all referencing table records when the referenced record is deleted.

CASCADE_BEHAVIOR_RESTRICT

Prevent the referenced table record from being deleted when referencing table records exist.

+
+
+

Module Contents

+
+
+PowerPlatform.Dataverse.common.constants.ODATA_TYPE_LOCALIZED_LABEL = 'Microsoft.Dynamics.CRM.LocalizedLabel'
+
+ +
+
+PowerPlatform.Dataverse.common.constants.ODATA_TYPE_LABEL = 'Microsoft.Dynamics.CRM.Label'
+
+ +
+
+PowerPlatform.Dataverse.common.constants.ODATA_TYPE_LOOKUP_ATTRIBUTE = 'Microsoft.Dynamics.CRM.LookupAttributeMetadata'
+
+ +
+
+PowerPlatform.Dataverse.common.constants.ODATA_TYPE_ONE_TO_MANY_RELATIONSHIP = 'Microsoft.Dynamics.CRM.OneToManyRelationshipMetadata'
+
+ +
+
+PowerPlatform.Dataverse.common.constants.ODATA_TYPE_MANY_TO_MANY_RELATIONSHIP = 'Microsoft.Dynamics.CRM.ManyToManyRelationshipMetadata'
+
+ +
+
+PowerPlatform.Dataverse.common.constants.CASCADE_BEHAVIOR_CASCADE = 'Cascade'
+

Perform the action on all referencing table records associated with the referenced table record.

+
+ +
+
+PowerPlatform.Dataverse.common.constants.CASCADE_BEHAVIOR_NO_CASCADE = 'NoCascade'
+

Do not apply the action to any referencing table records associated with the referenced table record.

+
+ +
+ +

Remove the value of the referencing column for all referencing table records when the referenced record is deleted.

+
+ +
+
+PowerPlatform.Dataverse.common.constants.CASCADE_BEHAVIOR_RESTRICT = 'Restrict'
+

Prevent the referenced table record from being deleted when referencing table records exist.

+
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/common/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/common/index.html new file mode 100644 index 00000000..276be10e --- /dev/null +++ b/docs_local/_build/autoapi/PowerPlatform/Dataverse/common/index.html @@ -0,0 +1,128 @@ + + + + + + + + PowerPlatform.Dataverse.common — PowerPlatform-Dataverse-Client 0.1.0b11 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

PowerPlatform.Dataverse.common

+

Common utilities and constants for the Dataverse SDK.

+

This module contains shared constants and utilities used across the SDK.

+
+

Submodules

+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/core/config/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/core/config/index.html new file mode 100644 index 00000000..cce886b3 --- /dev/null +++ b/docs_local/_build/autoapi/PowerPlatform/Dataverse/core/config/index.html @@ -0,0 +1,231 @@ + + + + + + + + PowerPlatform.Dataverse.core.config — PowerPlatform-Dataverse-Client 0.1.0b11 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

PowerPlatform.Dataverse.core.config

+

Dataverse client configuration.

+

Provides DataverseConfig, a lightweight +immutable container for locale and (reserved) HTTP tuning options plus the +convenience constructor from_env().

+
+

Classes

+ + + + + + + + + +

OperationContext

Caller-defined context appended to outbound User-Agent headers.

DataverseConfig

Configuration settings for Dataverse client operations.

+
+
+

Module Contents

+
+
+class PowerPlatform.Dataverse.core.config.OperationContext
+

Caller-defined context appended to outbound User-Agent headers.

+

The context string is validated to be semicolon-separated key=value pairs +using only allowed keys (app, skill, agent) with values from +closed allowlists. Free-form text, email addresses, PII, and unknown keys +are rejected.

+
+
Parameters:
+

user_agent_context (str) – Attribution string in key=value;key=value format.

+
+
Raises:
+

ValueError – If the string is empty, contains control characters, +does not match the required key=value format, or uses unknown +keys/values.

+
+
+
+
+user_agent_context: str
+
+ +
+ +
+
+class PowerPlatform.Dataverse.core.config.DataverseConfig
+

Configuration settings for Dataverse client operations.

+
+
Parameters:
+
    +
  • language_code (int) – LCID (Locale ID) for localized labels and messages. Default is 1033 (English - United States).

  • +
  • http_retries (int or None) – Optional maximum number of retry attempts for transient HTTP errors. Reserved for future use.

  • +
  • http_backoff (float or None) – Optional backoff multiplier (in seconds) between retry attempts. Reserved for future use.

  • +
  • http_timeout (float or None) – Optional request timeout in seconds. Reserved for future use.

  • +
  • log_config (LogConfig or None) – Optional local HTTP diagnostics logging configuration. +When provided, all HTTP requests and responses are logged to timestamped +.log files with automatic redaction of sensitive headers.

  • +
  • operation_context (OperationContext or None) – Optional caller-defined context object appended to the +outbound User-Agent header as a parenthesized comment. Intended for +plugin/tool attribution.

  • +
+
+
+
+
+language_code: int = 1033
+
+ +
+
+http_retries: int | None = None
+
+ +
+
+http_backoff: float | None = None
+
+ +
+
+http_timeout: float | None = None
+
+ +
+
+log_config: PowerPlatform.Dataverse.core.log_config.LogConfig | None = None
+
+ +
+
+operation_context: OperationContext | None = None
+
+ +
+
+classmethod from_env() DataverseConfig
+

Create a configuration instance with default settings.

+
+
Returns:
+

Configuration instance with default values.

+
+
Return type:
+

DataverseConfig

+
+
+
+ +
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/core/errors/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/core/errors/index.html new file mode 100644 index 00000000..a6fb74c9 --- /dev/null +++ b/docs_local/_build/autoapi/PowerPlatform/Dataverse/core/errors/index.html @@ -0,0 +1,304 @@ + + + + + + + + PowerPlatform.Dataverse.core.errors — PowerPlatform-Dataverse-Client 0.1.0b11 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

PowerPlatform.Dataverse.core.errors

+

Structured Dataverse exception hierarchy.

+

This module provides DataverseError and +specialized ValidationError, +MetadataError, +SQLParseError, and +HttpError for validation, metadata, +SQL parsing, and Web API HTTP failures.

+
+

Exceptions

+ + + + + + + + + + + + + + + + + + +

DataverseError

Base structured exception for the Dataverse SDK.

ValidationError

Exception raised for client-side validation failures.

MetadataError

Exception raised for metadata operation failures.

SQLParseError

Exception raised for SQL query parsing failures.

HttpError

Exception raised for HTTP request failures from the Dataverse Web API.

+
+
+

Module Contents

+
+
+exception PowerPlatform.Dataverse.core.errors.DataverseError(message: str, code: str, subcode: str | None = None, status_code: int | None = None, details: Dict[str, Any] | None = None, source: str | None = None, is_transient: bool = False)
+

Bases: Exception

+

Base structured exception for the Dataverse SDK.

+
+
Parameters:
+
    +
  • message (str) – Human-readable error message.

  • +
  • code (str) – Error category code (e.g. "validation_error", "http_error").

  • +
  • subcode (str | None) – Optional subcategory or specific error identifier.

  • +
  • status_code (int | None) – Optional HTTP status code if the error originated from an HTTP response.

  • +
  • details (dict | None) – Optional dictionary containing additional diagnostic information.

  • +
  • source (str) – Error source, either "client" or "server".

  • +
  • is_transient (bool) – Whether the error is potentially transient and may succeed on retry.

  • +
+
+
+

Initialize self. See help(type(self)) for accurate signature.

+
+
+message
+
+ +
+
+code
+
+ +
+
+subcode = None
+
+ +
+
+status_code = None
+
+ +
+
+details
+
+ +
+
+source = 'client'
+
+ +
+
+is_transient = False
+
+ +
+
+timestamp
+
+ +
+
+to_dict() Dict[str, Any]
+

Convert the error to a dictionary representation.

+
+
Returns:
+

Dictionary containing all error properties.

+
+
Return type:
+

dict

+
+
+
+ +
+ +
+
+exception PowerPlatform.Dataverse.core.errors.ValidationError(message: str, *, subcode: str | None = None, details: Dict[str, Any] | None = None)
+

Bases: DataverseError

+

Exception raised for client-side validation failures.

+
+
Parameters:
+
    +
  • message (str) – Human-readable validation error message.

  • +
  • subcode (str | None) – Optional specific validation error identifier.

  • +
  • details (dict | None) – Optional dictionary with additional validation context.

  • +
+
+
+

Initialize self. See help(type(self)) for accurate signature.

+
+ +
+
+exception PowerPlatform.Dataverse.core.errors.MetadataError(message: str, *, subcode: str | None = None, details: Dict[str, Any] | None = None)
+

Bases: DataverseError

+

Exception raised for metadata operation failures.

+
+
Parameters:
+
    +
  • message (str) – Human-readable metadata error message.

  • +
  • subcode (str | None) – Optional specific metadata error identifier.

  • +
  • details (dict | None) – Optional dictionary with additional metadata context.

  • +
+
+
+

Initialize self. See help(type(self)) for accurate signature.

+
+ +
+
+exception PowerPlatform.Dataverse.core.errors.SQLParseError(message: str, *, subcode: str | None = None, details: Dict[str, Any] | None = None)
+

Bases: DataverseError

+

Exception raised for SQL query parsing failures.

+
+
Parameters:
+
    +
  • message (str) – Human-readable SQL parsing error message.

  • +
  • subcode (str | None) – Optional specific SQL parsing error identifier.

  • +
  • details (dict | None) – Optional dictionary with SQL query context and parse information.

  • +
+
+
+

Initialize self. See help(type(self)) for accurate signature.

+
+ +
+
+exception PowerPlatform.Dataverse.core.errors.HttpError(message: str, status_code: int, is_transient: bool = False, subcode: str | None = None, service_error_code: str | None = None, correlation_id: str | None = None, client_request_id: str | None = None, service_request_id: str | None = None, traceparent: str | None = None, body_excerpt: str | None = None, retry_after: int | None = None, details: Dict[str, Any] | None = None)
+

Bases: DataverseError

+

Exception raised for HTTP request failures from the Dataverse Web API.

+
+
Parameters:
+
    +
  • message (str) – Human-readable HTTP error message, typically from the API error response.

  • +
  • status_code (int) – HTTP status code (e.g. 400, 404, 500).

  • +
  • is_transient (bool) – Whether the error is transient (429, 503, 504) and may succeed on retry.

  • +
  • subcode (str | None) – Optional HTTP status category (e.g. "4xx", "5xx").

  • +
  • service_error_code (str | None) – Optional Dataverse-specific error code from the API response.

  • +
  • correlation_id (str | None) – Optional client-generated correlation ID for tracking requests within an SDK call.

  • +
  • client_request_id (str | None) – Optional client-generated request ID injected into outbound headers.

  • +
  • service_request_id (str | None) – Optional x-ms-service-request-id value returned by Dataverse servers.

  • +
  • traceparent (str | None) – Optional W3C trace context for distributed tracing.

  • +
  • body_excerpt (str | None) – Optional excerpt of the response body for diagnostics.

  • +
  • retry_after (int | None) – Optional number of seconds to wait before retrying (from Retry-After header).

  • +
  • details (dict | None) – Optional additional diagnostic details.

  • +
+
+
+

Initialize self. See help(type(self)) for accurate signature.

+
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/core/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/core/index.html new file mode 100644 index 00000000..bd0cc295 --- /dev/null +++ b/docs_local/_build/autoapi/PowerPlatform/Dataverse/core/index.html @@ -0,0 +1,131 @@ + + + + + + + + PowerPlatform.Dataverse.core — PowerPlatform-Dataverse-Client 0.1.0b11 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

PowerPlatform.Dataverse.core

+

Core infrastructure components for the Dataverse SDK.

+

This module contains the foundational components including authentication, +configuration, HTTP client, and error handling.

+
+

Submodules

+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/core/log_config/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/core/log_config/index.html new file mode 100644 index 00000000..0e2c8348 --- /dev/null +++ b/docs_local/_build/autoapi/PowerPlatform/Dataverse/core/log_config/index.html @@ -0,0 +1,201 @@ + + + + + + + + PowerPlatform.Dataverse.core.log_config — PowerPlatform-Dataverse-Client 0.1.0b11 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

PowerPlatform.Dataverse.core.log_config

+

Local file logging configuration for Dataverse SDK HTTP diagnostics.

+

Provides LogConfig, an opt-in configuration for writing request/response +traces to .log files with automatic header redaction and timestamped filenames.

+
+

Classes

+ + + + + + +

LogConfig

Configuration for local HTTP diagnostics logging.

+
+
+

Module Contents

+
+
+class PowerPlatform.Dataverse.core.log_config.LogConfig
+

Configuration for local HTTP diagnostics logging.

+

When provided to DataverseClient via +DataverseConfig, every HTTP request +and response is logged to timestamped .log files in the specified folder. +Sensitive headers (e.g. Authorization) are automatically redacted.

+
+
Parameters:
+
    +
  • log_folder – Directory path for log files. Created automatically if missing. +Default: "./dataverse_logs"

  • +
  • log_file_prefix – Filename prefix. Timestamp is appended automatically. +Default: "dataverse"dataverse_20260310_143022.log

  • +
  • max_body_bytes – Maximum bytes of request/response body to capture. +0 (default) disables body capture. Enable only for active debugging +sessions — bodies may contain PII and sensitive business data.

  • +
  • redacted_headers – Header names (case-insensitive) whose values are +replaced with "[REDACTED]" in logs. Defaults include +Authorization, Proxy-Authorization, etc.

  • +
  • log_level – Python logging level name. Default: "DEBUG".

  • +
  • max_file_bytes – Max size per log file before rotation (bytes). +Default: 10_485_760 (10 MB).

  • +
  • backup_count – Number of rotated backup files to keep. Default: 5.

  • +
+
+
+
+
+log_folder: str = './dataverse_logs'
+
+ +
+
+log_file_prefix: str = 'dataverse'
+
+ +
+
+max_body_bytes: int = 0
+
+ +
+
+redacted_headers: FrozenSet[str]
+
+ +
+
+log_level: str = 'DEBUG'
+
+ +
+
+max_file_bytes: int = 10485760
+
+ +
+
+backup_count: int = 5
+
+ +
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/data/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/data/index.html new file mode 100644 index 00000000..645e7d97 --- /dev/null +++ b/docs_local/_build/autoapi/PowerPlatform/Dataverse/data/index.html @@ -0,0 +1,121 @@ + + + + + + + + PowerPlatform.Dataverse.data — PowerPlatform-Dataverse-Client 0.1.0b11 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

PowerPlatform.Dataverse.data

+

Data access layer for the Dataverse SDK.

+

This module contains OData protocol handling, CRUD operations, metadata management, +SQL query functionality, and file upload capabilities.

+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/extensions/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/extensions/index.html new file mode 100644 index 00000000..f703a817 --- /dev/null +++ b/docs_local/_build/autoapi/PowerPlatform/Dataverse/extensions/index.html @@ -0,0 +1,119 @@ + + + + + + + + PowerPlatform.Dataverse.extensions — PowerPlatform-Dataverse-Client 0.1.0b11 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

PowerPlatform.Dataverse.extensions

+

Optional extensions for the Dataverse SDK. Currently a placeholder.

+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/index.html new file mode 100644 index 00000000..3ed02f56 --- /dev/null +++ b/docs_local/_build/autoapi/PowerPlatform/Dataverse/index.html @@ -0,0 +1,133 @@ + + + + + + + + PowerPlatform.Dataverse — PowerPlatform-Dataverse-Client 0.1.0b11 documentation + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ + + + + + + \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/migration/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/migration/index.html new file mode 100644 index 00000000..80968581 --- /dev/null +++ b/docs_local/_build/autoapi/PowerPlatform/Dataverse/migration/index.html @@ -0,0 +1,126 @@ + + + + + + + + PowerPlatform.Dataverse.migration — PowerPlatform-Dataverse-Client 0.1.0b11 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

PowerPlatform.Dataverse.migration

+
+

Submodules

+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index.html new file mode 100644 index 00000000..136066a2 --- /dev/null +++ b/docs_local/_build/autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index.html @@ -0,0 +1,247 @@ + + + + + + + + PowerPlatform.Dataverse.migration.migrate_v0_to_v1 — PowerPlatform-Dataverse-Client 0.1.0b11 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

PowerPlatform.Dataverse.migration.migrate_v0_to_v1

+

DV-Python-SDK v0 -> v1 GA migration codemod.

+

Mechanically rewrites beta (0.1.0b*) call sites to their GA (1.0) equivalents +using LibCST (concrete syntax tree — preserves all whitespace and comments).

+

Usage:

+
pip install PowerPlatform-Dataverse-Client[migration]
+dataverse-migrate path/to/your/scripts/
+dataverse-migrate path/to/your/scripts/ --dry-run          # preview without writing
+dataverse-migrate path/to/your/scripts/ --client-var=svc   # if client is named 'svc'
+
+# Or via module for development installs:
+python -m PowerPlatform.Dataverse.migration.migrate_v0_to_v1 path/to/your/scripts/
+
+
+
+

Transformations applied

+

Builder methods (.filter_* -> .where(col(…)…)):

+
.filter_eq("col", v)               ->  .where(col("col") == v)
+.filter_ne("col", v)               ->  .where(col("col") != v)
+.filter_gt("col", v)               ->  .where(col("col") > v)
+.filter_ge("col", v)               ->  .where(col("col") >= v)
+.filter_lt("col", v)               ->  .where(col("col") < v)
+.filter_le("col", v)               ->  .where(col("col") <= v)
+.filter_contains("col", v)         ->  .where(col("col").contains(v))
+.filter_startswith("col", v)       ->  .where(col("col").startswith(v))
+.filter_endswith("col", v)         ->  .where(col("col").endswith(v))
+.filter_in("col", vals)            ->  .where(col("col").in_(vals))
+.filter_not_in("col", vals)        ->  .where(col("col").not_in(vals))
+.filter_null("col")                ->  .where(col("col").is_null())
+.filter_not_null("col")            ->  .where(col("col").is_not_null())
+.filter_between("col", lo, hi)     ->  .where(col("col").between(lo, hi))
+.filter_not_between("col", lo, hi) ->  .where(col("col").not_between(lo, hi))
+.filter_raw("expr")                ->  .where(raw("expr"))
+.filter("expr")                    ->  .where(raw("expr"))
+.execute(by_page=True)             ->  .execute_pages()
+.execute(by_page=False)            ->  .execute()  (flag removed)
+<builder_chain>.to_dataframe()     ->  <builder_chain>.execute().to_dataframe()
+    Inserts .execute() when the receiver is a recognised QueryBuilder chain
+    (contains .builder(), .select(), .where(), or a .filter_*() call).
+
+
+

Record namespace:

+
batch.records.get(t, id)     ->  batch.records.retrieve(t, id)
+
+
+

Top-level shortcuts (removed at GA):

+
client.create(t, d)           ->  client.records.create(t, d)
+client.update(t, id, d)       ->  client.records.update(t, id, d)
+client.delete(t, id)          ->  client.records.delete(t, id)
+client.get(t, id)             ->  client.records.get(t, id)  [deprecated; see manual section]
+client.query_sql(sql)         ->  client.query.sql(sql)
+client.get_table_info(t)      ->  client.tables.get(t)
+client.create_table(t, …)     ->  client.tables.create(t, …)
+client.delete_table(t)        ->  client.tables.delete(t)
+client.list_tables()          ->  client.tables.list()
+client.create_columns(t, …)   ->  client.tables.add_columns(t, …)
+client.delete_columns(t, …)   ->  client.tables.remove_columns(t, …)
+client.upload_file(…)         ->  client.files.upload(…)
+
+
+
+
Import management:

Adds from PowerPlatform.Dataverse.models.filters import col when a +.filter_* method is rewritten (if col is not already imported). +Adds raw to the same import when .filter_raw or .filter is rewritten.

+
+
NOT handled by this codemod (manual migration required):

execute(by_page=variable) -> manual review required (variable argument, not literal) +client.records.get(t, id) -> client.records.retrieve(t, id)

+
+

Return type changes: beta returns Record (raises on 404); GA retrieve() returns +Record | None. Callers that do not guard against None will fail silently.

+
+
+
client.records.get(t, kw=…) -> client.records.list(t, kw=…)

Return type changes: beta returns Iterable[List[Record]] (pages); GA list() +returns QueryResult (flat iterable over Records). Any for page in result: +for rec in page: iteration pattern breaks after a mechanical rename.

+
+
client.dataframe.get() -> client.query.builder(…).execute().to_dataframe()

Expression reconstruction requires understanding caller intent.

+
+
+

client.query.sql_select()/sql_join()/sql_joins() -> removed (no mechanical replacement)

+
+
+
+
+

Functions

+ + + + + + + + + + + + + + + +

find_manual_patterns(→ List[str])

Return descriptions of patterns in source that require manual migration.

migrate_source(→ str)

Parse source, apply transformations, return migrated source.

migrate_file(→ Tuple[bool, List[str]])

Migrate path in place. Returns (was_changed, manual_review_notes).

main(→ int)

+
+
+

Module Contents

+
+
+PowerPlatform.Dataverse.migration.migrate_v0_to_v1.find_manual_patterns(source: str, *, client_var: str = 'client') List[str]
+

Return descriptions of patterns in source that require manual migration.

+
+ +
+
+PowerPlatform.Dataverse.migration.migrate_v0_to_v1.migrate_source(source: str, *, client_var: str = 'client') str
+

Parse source, apply transformations, return migrated source.

+
+ +
+
+PowerPlatform.Dataverse.migration.migrate_v0_to_v1.migrate_file(path: pathlib.Path, *, dry_run: bool = False, client_var: str = 'client') Tuple[bool, List[str]]
+

Migrate path in place. Returns (was_changed, manual_review_notes).

+
+ +
+
+PowerPlatform.Dataverse.migration.migrate_v0_to_v1.main(argv: List[str] | None = None) int
+
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/batch/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/batch/index.html new file mode 100644 index 00000000..1acd508c --- /dev/null +++ b/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/batch/index.html @@ -0,0 +1,267 @@ + + + + + + + + PowerPlatform.Dataverse.models.batch — PowerPlatform-Dataverse-Client 0.1.0b11 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

PowerPlatform.Dataverse.models.batch

+

Public result types for batch operations.

+
+

Classes

+ + + + + + + + + +

BatchItemResponse

Response from a single operation within a batch request.

BatchResult

Result of executing a batch request.

+
+
+

Module Contents

+
+
+class PowerPlatform.Dataverse.models.batch.BatchItemResponse
+

Response from a single operation within a batch request.

+

Responses are returned in submission order. For operations added to a +changeset, responses appear in the changeset’s position in that order.

+
+
Parameters:
+
    +
  • status_code – HTTP status code for this operation (e.g. 204, 200, 400).

  • +
  • content_idContent-ID value from the changeset response part, if any.

  • +
  • entity_id – GUID extracted from the OData-EntityId response header. +Set for successful create (POST) operations.

  • +
  • data – Parsed JSON response body (e.g. for GET operations).

  • +
  • error_message – Error message when the operation failed.

  • +
  • error_code – Service error code when the operation failed.

  • +
+
+
+

Example:

+
for item in result.responses:
+    if item.is_success:
+        print(f"[OK] {item.status_code} entity_id={item.entity_id}")
+    else:
+        print(f"[ERR] {item.status_code}: {item.error_message}")
+
+
+
+
+status_code: int
+
+ +
+
+content_id: str | None = None
+
+ +
+
+entity_id: str | None = None
+
+ +
+
+data: Dict[str, Any] | None = None
+
+ +
+
+error_message: str | None = None
+
+ +
+
+error_code: str | None = None
+
+ +
+
+property is_success: bool
+

Return True when status_code is 2xx.

+
+ +
+ +
+
+class PowerPlatform.Dataverse.models.batch.BatchResult
+

Result of executing a batch request.

+

Contains one BatchItemResponse per HTTP operation submitted. +Operations that expand to multiple HTTP requests (e.g. add_columns +with three columns) contribute three entries.

+
+
Parameters:
+

responses – All responses in submission order.

+
+
+

Example:

+
result = client.batch.new().execute()
+print(f"Succeeded: {len(result.succeeded)}, Failed: {len(result.failed)}")
+for guid in result.entity_ids:
+    print(f"[OK] entity_id: {guid}")
+
+
+
+
+responses: List[BatchItemResponse] = []
+
+ +
+
+property succeeded: List[BatchItemResponse]
+

Responses with 2xx status codes.

+
+ +
+
+property failed: List[BatchItemResponse]
+

Responses with non-2xx status codes.

+
+ +
+
+property has_errors: bool
+

True when any response has a non-2xx status code.

+
+ +
+
+property entity_ids: List[str]
+

GUIDs extracted from OData-EntityId headers of successful responses.

+

Returns entity IDs from any successful (2xx) response that includes an +OData-EntityId header. Both individual POST (create) and +PATCH (update) operations return this header with the record’s GUID. +GET and DELETE operations do not.

+
+

Note

+

CreateMultiple and UpsertMultiple action responses do not +return per-record OData-EntityId headers. Their IDs are in the +JSON response body (data["Ids"]). Access them via:

+
for resp in result.succeeded:
+    if resp.data and "Ids" in resp.data:
+        bulk_ids = resp.data["Ids"]
+
+
+
+
+ +
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index.html new file mode 100644 index 00000000..2e3ae64b --- /dev/null +++ b/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index.html @@ -0,0 +1,201 @@ + + + + + + + + PowerPlatform.Dataverse.models.fetchxml_query — PowerPlatform-Dataverse-Client 0.1.0b11 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

PowerPlatform.Dataverse.models.fetchxml_query

+

FetchXmlQuery — inert query object returned by QueryOperations.fetchxml().

+
+

Classes

+ + + + + + +

FetchXmlQuery

Inert FetchXML query object. No HTTP request is made until

+
+
+

Module Contents

+
+
+class PowerPlatform.Dataverse.models.fetchxml_query.FetchXmlQuery(xml: str, entity_name: str, client: PowerPlatform.Dataverse.client.DataverseClient)
+

Inert FetchXML query object. No HTTP request is made until +execute() or execute_pages() is called.

+

Obtained via client.query.fetchxml(xml).

+
+
Parameters:
+
    +
  • xml – Stripped, well-formed FetchXML string.

  • +
  • entity_name – Entity schema name from the <entity> element.

  • +
  • client – Parent DataverseClient.

  • +
+
+
+
+
+execute() PowerPlatform.Dataverse.models.record.QueryResult
+

Execute the FetchXML query and return all results as a QueryResult.

+

Blocking — fetches all pages upfront and holds every record in memory before +returning. Simple for small-to-medium result sets; use execute_pages() +when the result set may be large or you want to process records as they arrive.

+
+
Returns:
+

All matching records across all pages.

+
+
Return type:
+

QueryResult

+
+
+

Example:

+
rows = client.query.fetchxml(xml).execute()
+df = rows.to_dataframe()
+
+
+
+ +
+
+execute_pages() Iterator[PowerPlatform.Dataverse.models.record.QueryResult]
+

Lazily yield one QueryResult per HTTP page.

+

Streaming — each iteration fires one HTTP request and yields one page. +Prefer over execute() when:

+
    +
  • The result set may be large and you do not want all records in memory at once.

  • +
  • You want early exit: stop iterating once you find what you need and the +remaining HTTP round-trips are skipped automatically.

  • +
  • You need per-page progress reporting or batched downstream writes.

  • +
+

One-shot — do not iterate more than once.

+
+
Returns:
+

Iterator of per-page QueryResult objects.

+
+
Return type:
+

Iterator[QueryResult]

+
+
+

Example:

+
for page in client.query.fetchxml(xml).execute_pages():
+    process(page.to_dataframe())
+
+
+
+ +
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/filters/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/filters/index.html new file mode 100644 index 00000000..a1666b0a --- /dev/null +++ b/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/filters/index.html @@ -0,0 +1,550 @@ + + + + + + + + PowerPlatform.Dataverse.models.filters — PowerPlatform-Dataverse-Client 0.1.0b11 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

PowerPlatform.Dataverse.models.filters

+

Composable OData filter expressions for the Dataverse SDK.

+

Provides an expression tree that compiles to OData $filter strings, +with Python operator overloads (&, |, ~) for composing +complex filter conditions.

+

Example:

+
from PowerPlatform.Dataverse.models.filters import col, raw
+
+# Preferred GA idiom — col() proxy
+expr = col("statecode") == 0
+print(expr.to_odata())  # statecode eq 0
+
+# Complex composition with OR and AND
+expr = (col("statecode") == 0) | (col("statecode") == 1) & (col("revenue") > 100000)
+print(expr.to_odata())
+
+# In / not-in
+expr = col("statecode").in_([0, 1, 2])
+print(expr.to_odata())
+# Microsoft.Dynamics.CRM.In(PropertyName='statecode',PropertyValues=["0","1","2"])
+
+# Raw OData escape hatch (no deprecation warning)
+expr = raw("Microsoft.Dynamics.CRM.Today(PropertyName='createdon')")
+
+# Negation
+expr = ~(col("statecode") == 1)
+print(expr.to_odata())  # not (statecode eq 1)
+
+
+
+

Classes

+ + + + + + + + + +

FilterExpression

Base class for composable OData filter expressions.

ColumnProxy

Fluent proxy for building OData filter expressions from a column name.

+
+
+

Functions

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

col(→ ColumnProxy)

Return a ColumnProxy for building filter expressions.

raw(→ FilterExpression)

Verbatim OData filter expression (passed through unchanged).

eq(→ FilterExpression)

Equality filter: column eq value.

ne(→ FilterExpression)

Not-equal filter: column ne value.

gt(→ FilterExpression)

Greater-than filter: column gt value.

ge(→ FilterExpression)

Greater-than-or-equal filter: column ge value.

lt(→ FilterExpression)

Less-than filter: column lt value.

le(→ FilterExpression)

Less-than-or-equal filter: column le value.

contains(→ FilterExpression)

Contains filter: contains(column, value).

startswith(→ FilterExpression)

Startswith filter: startswith(column, value).

endswith(→ FilterExpression)

Endswith filter: endswith(column, value).

between(→ FilterExpression)

Between filter: (column ge low and column le high).

is_null(→ FilterExpression)

Null check: column eq null.

is_not_null(→ FilterExpression)

Not-null check: column ne null.

filter_in(→ FilterExpression)

In filter using Microsoft.Dynamics.CRM.In.

not_in(→ FilterExpression)

Not-in filter using Microsoft.Dynamics.CRM.NotIn.

not_between(→ FilterExpression)

Not-between filter: not (column ge low and column le high).

+
+
+

Module Contents

+
+
+class PowerPlatform.Dataverse.models.filters.FilterExpression
+

Base class for composable OData filter expressions.

+

Supports Python operator overloads for logical composition:

+
    +
  • expr1 & expr2 produces (expr1 and expr2)

  • +
  • expr1 | expr2 produces (expr1 or expr2)

  • +
  • ~expr produces not (expr)

  • +
+
+
+abstractmethod to_odata() str
+

Compile this expression to an OData $filter string.

+
+ +
+ +
+
+class PowerPlatform.Dataverse.models.filters.ColumnProxy(name: str)
+

Fluent proxy for building OData filter expressions from a column name.

+

Returned by col(). Operator overloads and methods produce +FilterExpression instances that can be passed to +QueryBuilder.where().

+

Example:

+
from PowerPlatform.Dataverse.models.filters import col
+
+expr = col("statecode") == 0               # equality
+expr = col("revenue") > 1_000_000          # comparison
+expr = col("name").like("Contoso%")        # startswith
+expr = col("name").is_null()               # null check
+expr = col("statecode").in_([0, 1])        # in
+
+
+
+
+is_null() FilterExpression
+

Column equals null: column eq null.

+
+ +
+
+is_not_null() FilterExpression
+

Column not null: column ne null.

+
+ +
+
+in_(values: Collection[Any]) FilterExpression
+

In filter using Microsoft.Dynamics.CRM.In.

+
+
Parameters:
+

values – Non-empty collection of values.

+
+
Raises:
+

ValueError – If values is empty.

+
+
+
+ +
+
+not_in(values: Collection[Any]) FilterExpression
+

Not-in filter using Microsoft.Dynamics.CRM.NotIn.

+
+
Parameters:
+

values – Non-empty collection of values.

+
+
Raises:
+

ValueError – If values is empty.

+
+
+
+ +
+
+between(lo: Any, hi: Any) FilterExpression
+

Between filter: (column ge lo and column le hi).

+
+ +
+
+not_between(lo: Any, hi: Any) FilterExpression
+

Not-between filter: not (column ge lo and column le hi).

+
+ +
+
+contains(value: str) FilterExpression
+

Contains filter: contains(column, value).

+
+ +
+
+startswith(value: str) FilterExpression
+

Startswith filter: startswith(column, value).

+
+ +
+
+endswith(value: str) FilterExpression
+

Endswith filter: endswith(column, value).

+
+ +
+
+like(pattern: str) FilterExpression
+

Pattern-match filter compiled to the closest OData equivalent.

+
+
Parameters:
+

pattern – LIKE-style pattern string.

+
+
Raises:
+

ValueError – If the pattern cannot be reduced to a single OData function.

+
+
+
+ +
+
+not_like(pattern: str) FilterExpression
+

Negated pattern-match filter; mirrors like() rules then negates.

+
+
Parameters:
+

pattern – LIKE-style pattern string (same rules as like()).

+
+
Raises:
+

ValueError – If the pattern cannot be reduced to a single OData function.

+
+
+
+ +
+ +
+
+PowerPlatform.Dataverse.models.filters.col(name: str) ColumnProxy
+

Return a ColumnProxy for building filter expressions.

+

This is the preferred GA idiom for constructing filter expressions:

+
from PowerPlatform.Dataverse.models.filters import col
+
+expr = col("statecode") == 0
+expr = col("revenue") > 1_000_000
+expr = col("name").like("Contoso%")
+expr = col("statecode").in_([0, 1])
+expr = col("parentaccountid").is_null()
+
+
+
+
Parameters:
+

name – Column logical name (case-insensitive, will be lowercased).

+
+
Returns:
+

A ColumnProxy bound to the column.

+
+
Raises:
+

ValueError – If name is empty.

+
+
+
+ +
+
+PowerPlatform.Dataverse.models.filters.raw(filter_string: str) FilterExpression
+

Verbatim OData filter expression (passed through unchanged).

+

This function is not deprecated — it is the OData escape hatch with +no typed replacement.

+
+
Parameters:
+

filter_string – Raw OData filter string.

+
+
Returns:
+

A FilterExpression.

+
+
+

Example:

+
raw("Microsoft.Dynamics.CRM.Today(PropertyName='createdon')")
+
+
+
+ +
+
+PowerPlatform.Dataverse.models.filters.eq(column: str, value: Any) FilterExpression
+

Equality filter: column eq value.

+
+

Deprecated since version Use: col(column) == value instead.

+
+
+ +
+
+PowerPlatform.Dataverse.models.filters.ne(column: str, value: Any) FilterExpression
+

Not-equal filter: column ne value.

+
+

Deprecated since version Use: col(column) != value instead.

+
+
+ +
+
+PowerPlatform.Dataverse.models.filters.gt(column: str, value: Any) FilterExpression
+

Greater-than filter: column gt value.

+
+

Deprecated since version Use: col(column) > value instead.

+
+
+ +
+
+PowerPlatform.Dataverse.models.filters.ge(column: str, value: Any) FilterExpression
+

Greater-than-or-equal filter: column ge value.

+
+

Deprecated since version Use: col(column) >= value instead.

+
+
+ +
+
+PowerPlatform.Dataverse.models.filters.lt(column: str, value: Any) FilterExpression
+

Less-than filter: column lt value.

+
+

Deprecated since version Use: col(column) < value instead.

+
+
+ +
+
+PowerPlatform.Dataverse.models.filters.le(column: str, value: Any) FilterExpression
+

Less-than-or-equal filter: column le value.

+
+

Deprecated since version Use: col(column) <= value instead.

+
+
+ +
+
+PowerPlatform.Dataverse.models.filters.contains(column: str, value: str) FilterExpression
+

Contains filter: contains(column, value).

+
+

Deprecated since version Use: col(column).contains(value) instead.

+
+
+ +
+
+PowerPlatform.Dataverse.models.filters.startswith(column: str, value: str) FilterExpression
+

Startswith filter: startswith(column, value).

+
+

Deprecated since version Use: col(column).startswith(value) instead.

+
+
+ +
+
+PowerPlatform.Dataverse.models.filters.endswith(column: str, value: str) FilterExpression
+

Endswith filter: endswith(column, value).

+
+

Deprecated since version Use: col(column).endswith(value) instead.

+
+
+ +
+
+PowerPlatform.Dataverse.models.filters.between(column: str, low: Any, high: Any) FilterExpression
+

Between filter: (column ge low and column le high).

+
+

Deprecated since version Use: col(column).between(low, high) instead.

+
+
+ +
+
+PowerPlatform.Dataverse.models.filters.is_null(column: str) FilterExpression
+

Null check: column eq null.

+
+

Deprecated since version Use: col(column).is_null() instead.

+
+
+ +
+
+PowerPlatform.Dataverse.models.filters.is_not_null(column: str) FilterExpression
+

Not-null check: column ne null.

+
+

Deprecated since version Use: col(column).is_not_null() instead.

+
+
+ +
+
+PowerPlatform.Dataverse.models.filters.filter_in(column: str, values: Collection[Any]) FilterExpression
+

In filter using Microsoft.Dynamics.CRM.In.

+

Named filter_in because in is a Python keyword.

+
+

Deprecated since version Use: col(column).in_(values) instead.

+
+
+
Raises:
+

ValueError – If values is empty.

+
+
+
+ +
+
+PowerPlatform.Dataverse.models.filters.not_in(column: str, values: Collection[Any]) FilterExpression
+

Not-in filter using Microsoft.Dynamics.CRM.NotIn.

+
+

Deprecated since version Use: col(column).not_in(values) instead.

+
+
+
Raises:
+

ValueError – If values is empty.

+
+
+
+ +
+
+PowerPlatform.Dataverse.models.filters.not_between(column: str, low: Any, high: Any) FilterExpression
+

Not-between filter: not (column ge low and column le high).

+
+

Deprecated since version Use: col(column).not_between(low, high) instead.

+
+
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/index.html new file mode 100644 index 00000000..9f080066 --- /dev/null +++ b/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/index.html @@ -0,0 +1,148 @@ + + + + + + + + PowerPlatform.Dataverse.models — PowerPlatform-Dataverse-Client 0.1.0b11 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

PowerPlatform.Dataverse.models

+

Data models and type definitions for the Dataverse SDK.

+

Provides dataclasses and helpers for Dataverse entities:

+ +
+

Submodules

+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/labels/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/labels/index.html new file mode 100644 index 00000000..5d028e87 --- /dev/null +++ b/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/labels/index.html @@ -0,0 +1,233 @@ + + + + + + + + PowerPlatform.Dataverse.models.labels — PowerPlatform-Dataverse-Client 0.1.0b11 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

PowerPlatform.Dataverse.models.labels

+

Label models for Dataverse metadata.

+
+

Classes

+ + + + + + + + + +

LocalizedLabel

Represents a localized label with a language code.

Label

Represents a label that can have multiple localized versions.

+
+
+

Module Contents

+
+
+class PowerPlatform.Dataverse.models.labels.LocalizedLabel
+

Represents a localized label with a language code.

+
+
Parameters:
+
    +
  • label (str) – The text of the label.

  • +
  • language_code (int) – The language code (LCID), e.g., 1033 for English.

  • +
  • additional_properties (Optional[Dict[str, Any]]) – Optional dict of additional properties to include +in the Web API payload. These are merged last and can override default values.

  • +
+
+
+
+
+label: str
+
+ +
+
+language_code: int
+
+ +
+
+additional_properties: Dict[str, Any] | None = None
+
+ +
+
+to_dict() Dict[str, Any]
+

Convert to Web API JSON format.

+

Example:

+
>>> label = LocalizedLabel(label="Account", language_code=1033)
+>>> label.to_dict()
+{
+    '@odata.type': 'Microsoft.Dynamics.CRM.LocalizedLabel',
+    'Label': 'Account',
+    'LanguageCode': 1033
+}
+
+
+
+ +
+ +
+
+class PowerPlatform.Dataverse.models.labels.Label
+

Represents a label that can have multiple localized versions.

+
+
Parameters:
+
    +
  • localized_labels (List[LocalizedLabel]) – List of LocalizedLabel instances.

  • +
  • user_localized_label (Optional[LocalizedLabel]) – Optional user-specific localized label.

  • +
  • additional_properties (Optional[Dict[str, Any]]) – Optional dict of additional properties to include +in the Web API payload. These are merged last and can override default values.

  • +
+
+
+
+
+localized_labels: List[LocalizedLabel]
+
+ +
+
+user_localized_label: LocalizedLabel | None = None
+
+ +
+
+additional_properties: Dict[str, Any] | None = None
+
+ +
+
+to_dict() Dict[str, Any]
+

Convert to Web API JSON format.

+

Example:

+
>>> label = Label(localized_labels=[LocalizedLabel("Account", 1033)])
+>>> label.to_dict()
+{
+    '@odata.type': 'Microsoft.Dynamics.CRM.Label',
+    'LocalizedLabels': [
+        {'@odata.type': '...', 'Label': 'Account', 'LanguageCode': 1033}
+    ],
+    'UserLocalizedLabel': {'@odata.type': '...', 'Label': 'Account', ...}
+}
+
+
+
+ +
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/protocol/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/protocol/index.html new file mode 100644 index 00000000..2112eb2d --- /dev/null +++ b/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/protocol/index.html @@ -0,0 +1,202 @@ + + + + + + + + PowerPlatform.Dataverse.models.protocol — PowerPlatform-Dataverse-Client 0.1.0b11 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

PowerPlatform.Dataverse.models.protocol

+

DataverseModel structural Protocol for typed entity integration.

+
+

Classes

+ + + + + + +

DataverseModel

Structural Protocol enabling typed entity instances to be passed to

+
+
+

Module Contents

+
+
+class PowerPlatform.Dataverse.models.protocol.DataverseModel
+

Bases: Protocol

+

Structural Protocol enabling typed entity instances to be passed to +records.create() and records.update().

+

Implement this Protocol on any entity class (dataclass, Pydantic model, +hand-rolled) to enable it to be passed directly to CRUD operations without +specifying the table name or converting to dict manually.

+

Required class variables:

+
    +
  • __entity_logical_name__ — Dataverse logical entity name (e.g. "account")

  • +
  • __entity_set_name__ — OData entity set name (e.g. "accounts")

  • +
+

Required instance methods:

+
    +
  • to_dict() — return record payload as dict

  • +
  • from_dict(data) — classmethod to reconstruct from a response dict

  • +
+

Example:

+
from dataclasses import dataclass
+from PowerPlatform.Dataverse import DataverseModel
+
+@dataclass
+class Account:
+    __entity_logical_name__ = "account"
+    __entity_set_name__ = "accounts"
+    name: str = ""
+    telephone1: str = ""
+
+    def to_dict(self) -> dict:
+        return {"name": self.name, "telephone1": self.telephone1}
+
+    @classmethod
+    def from_dict(cls, data: dict) -> "Account":
+        return cls(
+            name=data.get("name", ""),
+            telephone1=data.get("telephone1", ""),
+        )
+
+# isinstance() works today — Protocol is runtime_checkable:
+assert isinstance(Account(), DataverseModel)
+
+# Type your own helpers against the Protocol now:
+def save(entity: DataverseModel) -> None:
+    data = entity.to_dict()
+    client.records.create(entity.__entity_logical_name__, data)
+
+
+
+

Note

+

Direct dispatch (client.records.create(entity) without a table name +or dict) is not yet supported and will be added in a future release.

+
+
+
+to_dict() dict
+

Return the record payload as a plain dictionary.

+
+ +
+
+classmethod from_dict(data: dict) DataverseModel
+

Reconstruct an instance from a response dictionary.

+
+ +
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/query_builder/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/query_builder/index.html new file mode 100644 index 00000000..7bdfe0f8 --- /dev/null +++ b/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/query_builder/index.html @@ -0,0 +1,504 @@ + + + + + + + + PowerPlatform.Dataverse.models.query_builder — PowerPlatform-Dataverse-Client 0.1.0b11 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

PowerPlatform.Dataverse.models.query_builder

+

Fluent query builder for constructing OData queries.

+

Provides a type-safe, discoverable interface for building complex queries +against Dataverse tables with method chaining.

+

Example:

+
# Via client (recommended) -- flat iteration over records
+from PowerPlatform.Dataverse.models import col
+
+for record in (client.query.builder("account")
+               .select("name", "revenue")
+               .where(col("statecode") == 0)
+               .where(col("revenue") > 1_000_000)
+               .order_by("revenue", descending=True)
+               .top(100)
+               .execute()):
+    print(record["name"])
+
+# With composable expression tree
+from PowerPlatform.Dataverse.models import col, raw
+
+for record in (client.query.builder("account")
+               .select("name", "revenue")
+               .where((col("statecode") == 0) | (col("statecode") == 1))
+               .where(col("revenue") > 100000)
+               .top(100)
+               .execute()):
+    print(record["name"])
+
+# Lazy paged iteration (one QueryResult per HTTP page)
+for page in (client.query.builder("account")
+             .select("name")
+             .execute_pages()):
+    process_batch(page)
+
+# Get results as a pandas DataFrame
+df = (client.query.builder("account")
+      .select("name", "telephone1")
+      .where(col("statecode") == 0)
+      .top(100)
+      .execute()
+      .to_dataframe())
+
+
+
+

Classes

+ + + + + + + + + + + + +

QueryParams

Typed dictionary returned by QueryBuilder.build().

ExpandOption

Structured options for an $expand navigation property.

QueryBuilder

Fluent interface for building and executing OData queries against a sync client.

+
+
+

Module Contents

+
+
+class PowerPlatform.Dataverse.models.query_builder.QueryParams
+

Bases: TypedDict

+

Typed dictionary returned by QueryBuilder.build().

+

Provides IDE autocomplete when passing build results to +client.records.list() manually.

+

Initialize self. See help(type(self)) for accurate signature.

+
+
+table: str
+
+ +
+
+select: List[str]
+
+ +
+
+filter: str
+
+ +
+
+orderby: List[str]
+
+ +
+
+expand: List[str]
+
+ +
+
+top: int
+
+ +
+
+page_size: int
+
+ +
+
+count: bool
+
+ +
+
+include_annotations: str
+
+ +
+ +
+
+class PowerPlatform.Dataverse.models.query_builder.ExpandOption(relation: str)
+

Structured options for an $expand navigation property.

+

Allows specifying nested $select, $filter, $orderby, and +$top options for a single navigation property expansion, following +the OData $expand syntax.

+
+
Parameters:
+

relation (str) – Navigation property name (case-sensitive).

+
+
+

Example:

+
# Expand Account_Tasks with nested options
+opt = (ExpandOption("Account_Tasks")
+       .select("subject", "createdon")
+       .filter("contains(subject,'Task')")
+       .order_by("createdon", descending=True)
+       .top(5))
+
+query = (client.query.builder("account")
+         .select("name")
+         .expand(opt)
+         .execute())
+
+
+
+
+relation
+
+ +
+
+select(*columns: str) ExpandOption
+

Select specific columns from the expanded entity.

+
+
Parameters:
+

columns – Column names to select.

+
+
Returns:
+

Self for method chaining.

+
+
+
+ +
+
+filter(filter_str: str) ExpandOption
+

Filter the expanded collection.

+
+
Parameters:
+

filter_str – OData $filter expression.

+
+
Returns:
+

Self for method chaining.

+
+
+
+ +
+
+order_by(column: str, descending: bool = False) ExpandOption
+

Sort the expanded collection.

+
+
Parameters:
+
    +
  • column – Column name to sort by.

  • +
  • descending – Sort descending if True.

  • +
+
+
Returns:
+

Self for method chaining.

+
+
+
+ +
+
+top(count: int) ExpandOption
+

Limit expanded results.

+
+
Parameters:
+

count – Maximum number of expanded records.

+
+
Returns:
+

Self for method chaining.

+
+
+
+ +
+
+to_odata() str
+

Compile to OData $expand syntax.

+
+
Returns:
+

OData expand string like "Nav($select=col1,col2;$filter=...)"

+
+
Return type:
+

str

+
+
+
+ +
+ +
+
+class PowerPlatform.Dataverse.models.query_builder.QueryBuilder(table: str)
+

Bases: _QueryBuilderBase

+

Fluent interface for building and executing OData queries against a sync client.

+

Provides method chaining for constructing complex queries with +composable filter expressions. Can be used standalone (via build()) +or bound to a client (via execute()).

+
+
Parameters:
+

table (str) – Table schema name to query.

+
+
Raises:
+

ValueError – If table is empty.

+
+
+

Example

+

Standalone query construction:

+
from PowerPlatform.Dataverse.models import col
+
+query = (QueryBuilder("account")
+         .select("name")
+         .where(col("statecode") == 0)
+         .top(10))
+params = query.build()
+# {"table": "account", "select": ["name"],
+#  "filter": "statecode eq 0", "top": 10}
+
+
+
+
+execute(*, by_page=_BY_PAGE_UNSET) PowerPlatform.Dataverse.models.record.QueryResult | Iterator[PowerPlatform.Dataverse.models.record.QueryResult]
+

Execute the query and return results.

+

Returns a QueryResult +with all pages collected. Use execute_pages() for lazy per-page +iteration.

+

This method is only available when the QueryBuilder was created +via client.query.builder(table). Standalone QueryBuilder +instances should use build() to get parameters and pass them +to client.records.list() manually.

+

At least one of select(), where(), or top() must be +called before execute(); otherwise a ValueError is +raised to prevent accidental full-table scans.

+
+

Deprecated since version The: by_page parameter is deprecated. Use execute_pages() +for lazy per-page iteration, or plain execute() (no flag) for +the default eager result.

+
+
+
Returns:
+

QueryResult +with all pages collected (default), or page iterator (deprecated +by_page=True).

+
+
Return type:
+

QueryResult or Iterator[QueryResult]

+
+
Raises:
+
    +
  • ValueError – If no select, where, or top +constraint has been set.

  • +
  • RuntimeError – If the query was not created via +client.query.builder().

  • +
+
+
+

Example:

+
from PowerPlatform.Dataverse.models import col
+
+for record in (client.query.builder("account")
+               .select("name")
+               .where(col("statecode") == 0)
+               .execute()):
+    print(record["name"])
+
+
+
+ +
+
+execute_pages() Iterator[PowerPlatform.Dataverse.models.record.QueryResult]
+

Lazily yield one QueryResult +per HTTP page.

+

Each iteration triggers a network request via @odata.nextLink. +One-shot — do not iterate more than once.

+

At least one of select(), where(), or top() must be +called before execute_pages(); otherwise a ValueError is +raised to prevent accidental full-table scans.

+
+
Returns:
+

Iterator of per-page QueryResult.

+
+
Return type:
+

Iterator[QueryResult]

+
+
Raises:
+
    +
  • ValueError – If no select, where, or top +constraint has been set.

  • +
  • RuntimeError – If the query was not created via +client.query.builder().

  • +
+
+
+

Example:

+
from PowerPlatform.Dataverse.models import col
+
+for page in (client.query.builder("account")
+             .select("name")
+             .where(col("statecode") == 0)
+             .execute_pages()):
+    process(page.to_dataframe())
+
+
+
+ +
+
+to_dataframe() pandas.DataFrame
+

Execute the query and return results as a pandas DataFrame.

+
+

Deprecated since version Use: QueryBuilder.execute().to_dataframe() instead. +QueryBuilder.to_dataframe() will be removed in a future release.

+
+

All pages are consolidated into a single DataFrame.

+

This method is only available when the QueryBuilder was created +via client.query.builder(table).

+

At least one of select(), where(), or top() must be +called before to_dataframe(); otherwise a ValueError +is raised to prevent accidental full-table scans.

+
+
Returns:
+

DataFrame containing all matching records. Returns an empty +DataFrame when no records match.

+
+
Return type:
+

DataFrame

+
+
Raises:
+
    +
  • ValueError – If no select, where, or top +constraint has been set.

  • +
  • RuntimeError – If the query was not created via +client.query.builder().

  • +
+
+
+

Example:

+
from PowerPlatform.Dataverse.models import col
+
+df = (client.query.builder("account")
+      .select("name", "telephone1")
+      .where(col("statecode") == 0)
+      .top(100)
+      .execute()
+      .to_dataframe())
+
+
+
+ +
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/record/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/record/index.html new file mode 100644 index 00000000..1474cd36 --- /dev/null +++ b/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/record/index.html @@ -0,0 +1,273 @@ + + + + + + + + PowerPlatform.Dataverse.models.record — PowerPlatform-Dataverse-Client 0.1.0b11 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

PowerPlatform.Dataverse.models.record

+

Record data model for Dataverse entities.

+
+

Classes

+ + + + + + + + + +

Record

Strongly-typed Dataverse record with dict-like backward compatibility.

QueryResult

Iterable wrapper around a list of Record objects.

+
+
+

Module Contents

+
+
+class PowerPlatform.Dataverse.models.record.Record
+

Strongly-typed Dataverse record with dict-like backward compatibility.

+

Wraps raw OData response data into a structured object while preserving +result["key"] access patterns for existing code.

+
+
Parameters:
+
    +
  • id (str) – Record GUID. Empty string if not extracted (e.g. paginated +results, SQL queries).

  • +
  • table (str) – Table schema name used in the request.

  • +
  • data (dict) – Record field data as key-value pairs.

  • +
  • etag (str or None) – ETag for optimistic concurrency, extracted from +@odata.etag in the API response.

  • +
+
+
+

Example:

+
record = client.records.get("account", account_id, select=["name"])
+print(record.id)          # structured access
+print(record["name"])     # dict-like access (backward compat)
+
+
+
+
+id: str = ''
+
+ +
+
+table: str = ''
+
+ +
+
+data: Dict[str, Any]
+
+ +
+
+etag: str | None = None
+
+ +
+
+get(key: str, default: Any = None) Any
+

Return value for key, or default if not present.

+
+ +
+
+keys() KeysView[str]
+

Return data keys.

+
+ +
+
+values() ValuesView[Any]
+

Return data values.

+
+ +
+
+items() ItemsView[str, Any]
+

Return data items.

+
+ +
+
+classmethod from_api_response(table: str, response_data: Dict[str, Any], *, record_id: str = '') Record
+

Create a Record from a raw OData API response.

+

Strips @odata.* annotation keys from the data and extracts the +@odata.etag value if present.

+
+
Parameters:
+
    +
  • table (str) – Table schema name.

  • +
  • response_data (dict) – Raw JSON dict from the OData response.

  • +
  • record_id (str) – Known record GUID. Pass explicitly when available +(e.g. single-record get). Defaults to empty string.

  • +
+
+
Return type:
+

Record

+
+
+
+ +
+
+to_dict() Dict[str, Any]
+

Return a plain dict copy of the record data (excludes metadata).

+
+ +
+ +
+
+class PowerPlatform.Dataverse.models.record.QueryResult(records: List[Record])
+

Iterable wrapper around a list of Record objects.

+

Returned by execute() +(flat mode) and list().

+

Backward-compatible: for r in result continues to work without change.

+
+
Parameters:
+

records (list[Record]) – Collected records from all pages.

+
+
+
+
+records: List[Record]
+
+ +
+
+first() Record | None
+

Return the first record, or None if the result is empty.

+
+ +
+
+to_dataframe() Any
+

Return all records as a pandas DataFrame.

+
+
Raises:
+

ImportError – If pandas is not installed.

+
+
Return type:
+

DataFrame

+
+
+
+ +
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/relationship/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/relationship/index.html new file mode 100644 index 00000000..efe93ab4 --- /dev/null +++ b/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/relationship/index.html @@ -0,0 +1,586 @@ + + + + + + + + PowerPlatform.Dataverse.models.relationship — PowerPlatform-Dataverse-Client 0.1.0b11 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

PowerPlatform.Dataverse.models.relationship

+

Relationship models for Dataverse (input and output).

+
+

Classes

+ + + + + + + + + + + + + + + + + + +

CascadeConfiguration

Defines cascade behavior for relationship operations.

LookupAttributeMetadata

Metadata for a lookup attribute.

OneToManyRelationshipMetadata

Metadata for a one-to-many entity relationship.

ManyToManyRelationshipMetadata

Metadata for a many-to-many entity relationship.

RelationshipInfo

Typed return model for relationship metadata.

+
+
+

Module Contents

+
+
+class PowerPlatform.Dataverse.models.relationship.CascadeConfiguration
+

Defines cascade behavior for relationship operations.

+
+
Parameters:
+
    +
  • assign (str) – Cascade behavior for assign operations.

  • +
  • delete (str) – Cascade behavior for delete operations.

  • +
  • merge (str) – Cascade behavior for merge operations.

  • +
  • reparent (str) – Cascade behavior for reparent operations.

  • +
  • share (str) – Cascade behavior for share operations.

  • +
  • unshare (str) – Cascade behavior for unshare operations.

  • +
  • additional_properties (Optional[Dict[str, Any]]) – Optional dict of additional properties to include +in the Web API payload (e.g., “Archive”, “RollupView”). These are merged +last and can override default values.

  • +
+
+
+
+
Valid values for each parameter:
    +
  • “Cascade”: Perform the operation on all related records

  • +
  • “NoCascade”: Do not perform the operation on related records

  • +
  • “RemoveLink”: Remove the relationship link but keep the records

  • +
  • “Restrict”: Prevent the operation if related records exist

  • +
+
+
+
+
+assign: str = 'NoCascade'
+
+ +
+
+delete: str = 'RemoveLink'
+
+ +
+
+merge: str = 'NoCascade'
+
+ +
+
+reparent: str = 'NoCascade'
+
+ +
+
+share: str = 'NoCascade'
+
+ +
+
+unshare: str = 'NoCascade'
+
+ +
+
+additional_properties: Dict[str, Any] | None = None
+
+ +
+
+to_dict() Dict[str, Any]
+

Convert to Web API JSON format.

+

Example:

+
>>> config = CascadeConfiguration(delete="Cascade", assign="NoCascade")
+>>> config.to_dict()
+{
+    'Assign': 'NoCascade',
+    'Delete': 'Cascade',
+    'Merge': 'NoCascade',
+    'Reparent': 'NoCascade',
+    'Share': 'NoCascade',
+    'Unshare': 'NoCascade'
+}
+
+
+
+ +
+ +
+
+class PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata
+

Metadata for a lookup attribute.

+
+
Parameters:
+
    +
  • schema_name (str) – Schema name for the attribute (e.g., “new_AccountId”).

  • +
  • display_name (Label) – Display name for the attribute.

  • +
  • description (Optional[Label]) – Optional description of the attribute.

  • +
  • required_level (str) – Requirement level for the attribute.

  • +
  • additional_properties (Optional[Dict[str, Any]]) – Optional dict of additional properties to include +in the Web API payload. Useful for setting properties like “Targets” (to +specify which entity types the lookup can reference), “LogicalName”, +“IsSecured”, “IsValidForAdvancedFind”, etc. These are merged last and +can override default values.

  • +
+
+
+
+
Valid required_level values:
    +
  • “None”: The attribute is optional

  • +
  • “Recommended”: The attribute is recommended

  • +
  • “ApplicationRequired”: The attribute is required

  • +
+
+
+
+
+schema_name: str
+
+ +
+
+display_name: PowerPlatform.Dataverse.models.labels.Label
+
+ +
+
+description: PowerPlatform.Dataverse.models.labels.Label | None = None
+
+ +
+
+required_level: str = 'None'
+
+ +
+
+additional_properties: Dict[str, Any] | None = None
+
+ +
+
+to_dict() Dict[str, Any]
+

Convert to Web API JSON format.

+

Example:

+
>>> lookup = LookupAttributeMetadata(
+...     schema_name="new_AccountId",
+...     display_name=Label([LocalizedLabel("Account", 1033)])
+... )
+>>> lookup.to_dict()
+{
+    '@odata.type': 'Microsoft.Dynamics.CRM.LookupAttributeMetadata',
+    'SchemaName': 'new_AccountId',
+    'AttributeType': 'Lookup',
+    'AttributeTypeName': {'Value': 'LookupType'},
+    'DisplayName': {...},
+    'RequiredLevel': {'Value': 'None', 'CanBeChanged': True, ...}
+}
+
+
+
+ +
+ +
+
+class PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata
+

Metadata for a one-to-many entity relationship.

+
+
Parameters:
+
    +
  • schema_name (str) – Schema name for the relationship (e.g., “new_Account_Orders”).

  • +
  • referenced_entity (str) – Logical name of the referenced (parent) entity.

  • +
  • referencing_entity (str) – Logical name of the referencing (child) entity.

  • +
  • referenced_attribute (str) – Attribute on the referenced entity (typically the primary key).

  • +
  • cascade_configuration (CascadeConfiguration) – Cascade behavior configuration.

  • +
  • referencing_attribute (Optional[str]) – Optional name for the referencing attribute (usually auto-generated).

  • +
  • additional_properties (Optional[Dict[str, Any]]) – Optional dict of additional properties to include +in the Web API payload. Useful for setting inherited properties like +“IsValidForAdvancedFind”, “IsCustomizable”, “SecurityTypes”, etc. +These are merged last and can override default values.

  • +
+
+
+
+
+schema_name: str
+
+ +
+
+referenced_entity: str
+
+ +
+
+referencing_entity: str
+
+ +
+
+referenced_attribute: str
+
+ +
+
+cascade_configuration: CascadeConfiguration
+
+ +
+
+referencing_attribute: str | None = None
+
+ +
+
+additional_properties: Dict[str, Any] | None = None
+
+ +
+
+to_dict() Dict[str, Any]
+

Convert to Web API JSON format.

+

Example:

+
>>> rel = OneToManyRelationshipMetadata(
+...     schema_name="new_account_orders",
+...     referenced_entity="account",
+...     referencing_entity="new_order",
+...     referenced_attribute="accountid"
+... )
+>>> rel.to_dict()
+{
+    '@odata.type': 'Microsoft.Dynamics.CRM.OneToManyRelationshipMetadata',
+    'SchemaName': 'new_account_orders',
+    'ReferencedEntity': 'account',
+    'ReferencingEntity': 'new_order',
+    'ReferencedAttribute': 'accountid',
+    'CascadeConfiguration': {...}
+}
+
+
+
+ +
+ +
+
+class PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata
+

Metadata for a many-to-many entity relationship.

+
+
Parameters:
+
    +
  • schema_name (str) – Schema name for the relationship.

  • +
  • entity1_logical_name (str) – Logical name of the first entity.

  • +
  • entity2_logical_name (str) – Logical name of the second entity.

  • +
  • intersect_entity_name (Optional[str]) – Name for the intersect table (defaults to schema_name if not provided).

  • +
  • additional_properties (Optional[Dict[str, Any]]) – Optional dict of additional properties to include +in the Web API payload. Useful for setting inherited properties like +“IsValidForAdvancedFind”, “IsCustomizable”, “SecurityTypes”, or direct +properties like “Entity1NavigationPropertyName”. These are merged last +and can override default values.

  • +
+
+
+
+
+schema_name: str
+
+ +
+
+entity1_logical_name: str
+
+ +
+
+entity2_logical_name: str
+
+ +
+
+intersect_entity_name: str | None = None
+
+ +
+
+additional_properties: Dict[str, Any] | None = None
+
+ +
+
+to_dict() Dict[str, Any]
+

Convert to Web API JSON format.

+

Example:

+
>>> rel = ManyToManyRelationshipMetadata(
+...     schema_name="new_account_contact",
+...     entity1_logical_name="account",
+...     entity2_logical_name="contact"
+... )
+>>> rel.to_dict()
+{
+    '@odata.type': 'Microsoft.Dynamics.CRM.ManyToManyRelationshipMetadata',
+    'SchemaName': 'new_account_contact',
+    'Entity1LogicalName': 'account',
+    'Entity2LogicalName': 'contact',
+    'IntersectEntityName': 'new_account_contact'
+}
+
+
+
+ +
+ +
+
+class PowerPlatform.Dataverse.models.relationship.RelationshipInfo
+

Typed return model for relationship metadata.

+

Returned by create_one_to_many_relationship(), +create_many_to_many_relationship(), +get_relationship(), and +create_lookup_field().

+
+
Parameters:
+
    +
  • relationship_id (str or None) – Relationship metadata GUID.

  • +
  • relationship_schema_name (str) – Relationship schema name.

  • +
  • relationship_type (str) – Either "one_to_many" or "many_to_many".

  • +
  • lookup_schema_name (str or None) – Lookup field schema name (one-to-many only).

  • +
  • referenced_entity (str or None) – Parent entity logical name (one-to-many only).

  • +
  • referencing_entity (str or None) – Child entity logical name (one-to-many only).

  • +
  • entity1_logical_name (str or None) – First entity logical name (many-to-many only).

  • +
  • entity2_logical_name (str or None) – Second entity logical name (many-to-many only).

  • +
+
+
+

Example:

+
result = client.tables.create_one_to_many_relationship(lookup, relationship)
+print(result.relationship_schema_name)
+print(result.lookup_schema_name)
+
+
+
+
+relationship_id: str | None = None
+
+ +
+
+relationship_schema_name: str = ''
+
+ +
+
+relationship_type: str = ''
+
+ +
+
+lookup_schema_name: str | None = None
+
+ +
+
+referenced_entity: str | None = None
+
+ +
+
+referencing_entity: str | None = None
+
+ +
+
+entity1_logical_name: str | None = None
+
+ +
+
+entity2_logical_name: str | None = None
+
+ +
+
+classmethod from_one_to_many(*, relationship_id: str | None, relationship_schema_name: str, lookup_schema_name: str, referenced_entity: str, referencing_entity: str) RelationshipInfo
+

Create from a one-to-many relationship result.

+
+
Parameters:
+
    +
  • relationship_id (str or None) – Relationship metadata GUID.

  • +
  • relationship_schema_name (str) – Relationship schema name.

  • +
  • lookup_schema_name (str) – Lookup field schema name.

  • +
  • referenced_entity (str) – Parent entity logical name.

  • +
  • referencing_entity (str) – Child entity logical name.

  • +
+
+
Return type:
+

RelationshipInfo

+
+
+
+ +
+
+classmethod from_many_to_many(*, relationship_id: str | None, relationship_schema_name: str, entity1_logical_name: str, entity2_logical_name: str) RelationshipInfo
+

Create from a many-to-many relationship result.

+
+
Parameters:
+
    +
  • relationship_id (str or None) – Relationship metadata GUID.

  • +
  • relationship_schema_name (str) – Relationship schema name.

  • +
  • entity1_logical_name (str) – First entity logical name.

  • +
  • entity2_logical_name (str) – Second entity logical name.

  • +
+
+
Return type:
+

RelationshipInfo

+
+
+
+ +
+
+classmethod from_api_response(response_data: Dict[str, Any]) RelationshipInfo
+

Create from a raw Dataverse Web API response.

+

Detects one-to-many vs many-to-many from the @odata.type field +in the response and maps PascalCase keys to snake_case attributes. +Dataverse only supports these two relationship types; an unrecognized +@odata.type raises ValueError.

+
+
Parameters:
+

response_data (dict) – Raw relationship metadata from the Web API.

+
+
Return type:
+

RelationshipInfo

+
+
Raises:
+

ValueError – If the @odata.type is not a recognized +relationship type.

+
+
+
+ +
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/table_info/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/table_info/index.html new file mode 100644 index 00000000..26f39001 --- /dev/null +++ b/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/table_info/index.html @@ -0,0 +1,403 @@ + + + + + + + + PowerPlatform.Dataverse.models.table_info — PowerPlatform-Dataverse-Client 0.1.0b11 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

PowerPlatform.Dataverse.models.table_info

+

Table and column metadata models for Dataverse.

+
+

Classes

+ + + + + + + + + + + + +

ColumnInfo

Column metadata from a Dataverse table definition.

TableInfo

Table metadata with dict-like backward compatibility.

AlternateKeyInfo

Alternate key metadata for a Dataverse table.

+
+
+

Module Contents

+
+
+class PowerPlatform.Dataverse.models.table_info.ColumnInfo
+

Column metadata from a Dataverse table definition.

+
+
Parameters:
+
    +
  • schema_name (str) – Column schema name (e.g. "new_Price").

  • +
  • logical_name (str) – Column logical name (lowercase).

  • +
  • type (str) – Column type string (e.g. "String", "Integer").

  • +
  • is_primary (bool) – Whether this is the primary name column.

  • +
  • is_required (bool) – Whether the column is required.

  • +
  • max_length (int or None) – Maximum length for string columns.

  • +
  • display_name (str or None) – Human-readable display name.

  • +
  • description (str or None) – Column description.

  • +
+
+
+
+
+schema_name: str = ''
+
+ +
+
+logical_name: str = ''
+
+ +
+
+type: str = ''
+
+ +
+
+is_primary: bool = False
+
+ +
+
+is_required: bool = False
+
+ +
+
+max_length: int | None = None
+
+ +
+
+display_name: str | None = None
+
+ +
+
+description: str | None = None
+
+ +
+
+classmethod from_api_response(response_data: Dict[str, Any]) ColumnInfo
+

Create from a raw Dataverse AttributeMetadata API response.

+
+
Parameters:
+

response_data (dict) – Raw attribute metadata dict (PascalCase keys).

+
+
Return type:
+

ColumnInfo

+
+
+
+ +
+ +
+
+class PowerPlatform.Dataverse.models.table_info.TableInfo
+

Table metadata with dict-like backward compatibility.

+

Supports both new attribute access (info.schema_name) and legacy +dict-key access (info["table_schema_name"]) for backward +compatibility with code written against the raw dict API.

+
+
Parameters:
+
    +
  • schema_name (str) – Table schema name (e.g. "Account").

  • +
  • logical_name (str) – Table logical name (lowercase).

  • +
  • entity_set_name (str) – OData entity set name.

  • +
  • metadata_id (str) – Metadata GUID.

  • +
  • display_name (str or None) – Human-readable display name.

  • +
  • description (str or None) – Table description.

  • +
  • columns (list[ColumnInfo] or None) – Column metadata (when retrieved).

  • +
  • columns_created (list[str] or None) – Column schema names created with the table.

  • +
+
+
+

Example:

+
info = client.tables.create("new_Product", {"new_Price": "decimal"})
+print(info.schema_name)              # new attribute access
+print(info["table_schema_name"])     # legacy dict-key access
+
+
+
+
+schema_name: str = ''
+
+ +
+
+logical_name: str = ''
+
+ +
+
+entity_set_name: str = ''
+
+ +
+
+metadata_id: str = ''
+
+ +
+
+primary_name_attribute: str | None = None
+
+ +
+
+primary_id_attribute: str | None = None
+
+ +
+
+display_name: str | None = None
+
+ +
+
+description: str | None = None
+
+ +
+
+columns: List[ColumnInfo] | None = None
+
+ +
+
+columns_created: List[str] | None = None
+
+ +
+
+get(key: str, default: Any = None) Any
+

Return value for key, or default if not present.

+
+ +
+
+keys() KeysView[str]
+

Return legacy dict keys.

+
+ +
+
+values() List[Any]
+

Return values corresponding to legacy dict keys.

+
+ +
+
+items() List[tuple]
+

Return (legacy_key, value) pairs.

+
+ +
+
+classmethod from_dict(data: Dict[str, Any]) TableInfo
+

Create from an SDK internal dict (snake_case keys).

+

This handles the dict format returned by _create_table and +_get_table_info in the OData layer.

+
+
Parameters:
+

data (dict) – Dictionary with SDK snake_case keys.

+
+
Return type:
+

TableInfo

+
+
+
+ +
+
+classmethod from_api_response(response_data: Dict[str, Any]) TableInfo
+

Create from a raw Dataverse EntityDefinition API response.

+
+
Parameters:
+

response_data (dict) – Raw entity metadata dict (PascalCase keys).

+
+
Return type:
+

TableInfo

+
+
+
+ +
+
+to_dict() Dict[str, Any]
+

Return a dict with legacy keys for backward compatibility.

+
+ +
+ +
+
+class PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo
+

Alternate key metadata for a Dataverse table.

+
+
Parameters:
+
    +
  • metadata_id (str) – Key metadata GUID.

  • +
  • schema_name (str) – Key schema name.

  • +
  • key_attributes (list[str]) – List of column logical names that compose the key.

  • +
  • status (str) – Index creation status ("Active", "Pending", "InProgress", "Failed").

  • +
+
+
+
+
+metadata_id: str = ''
+
+ +
+
+schema_name: str = ''
+
+ +
+
+key_attributes: List[str] = []
+
+ +
+
+status: str = ''
+
+ +
+
+classmethod from_api_response(response_data: Dict[str, Any]) AlternateKeyInfo
+

Create from raw EntityKeyMetadata API response.

+
+
Parameters:
+

response_data (dict) – Raw key metadata dictionary from the Web API.

+
+
Return type:
+

AlternateKeyInfo

+
+
+
+ +
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/upsert/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/upsert/index.html new file mode 100644 index 00000000..cc9b7d70 --- /dev/null +++ b/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/upsert/index.html @@ -0,0 +1,171 @@ + + + + + + + + PowerPlatform.Dataverse.models.upsert — PowerPlatform-Dataverse-Client 0.1.0b11 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

PowerPlatform.Dataverse.models.upsert

+

Upsert data models for the Dataverse SDK.

+
+

Classes

+ + + + + + +

UpsertItem

Represents a single upsert operation targeting a record by its alternate key.

+
+
+

Module Contents

+
+
+class PowerPlatform.Dataverse.models.upsert.UpsertItem
+

Represents a single upsert operation targeting a record by its alternate key.

+

Used with upsert() +to upsert one or more records identified by alternate keys rather than primary GUIDs.

+
+
Parameters:
+
    +
  • alternate_key (dict[str, Any]) – Dictionary mapping alternate key attribute names to their values. +String values are automatically quoted and escaped in the OData URL. Integer and +other non-string values are included without quotes.

  • +
  • record (dict[str, Any]) – Dictionary of attribute names to values for the record payload. +Keys are automatically lowercased. Picklist labels are resolved to integer option +values when a matching option set is found.

  • +
+
+
+

Example:

+
item = UpsertItem(
+    alternate_key={"accountnumber": "ACC-001", "address1_postalcode": "98052"},
+    record={"name": "Contoso Ltd", "telephone1": "555-0100"},
+)
+
+
+
+
+alternate_key: Dict[str, Any]
+
+ +
+
+record: Dict[str, Any]
+
+ +
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/batch/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/batch/index.html new file mode 100644 index 00000000..b09e2f70 --- /dev/null +++ b/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/batch/index.html @@ -0,0 +1,932 @@ + + + + + + + + PowerPlatform.Dataverse.operations.batch — PowerPlatform-Dataverse-Client 0.1.0b11 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

PowerPlatform.Dataverse.operations.batch

+

Batch operation namespaces for the Dataverse SDK.

+
+

Classes

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +

ChangeSetRecordOperations

Record write operations available inside a ChangeSet.

ChangeSet

A transactional group of single-record write operations.

BatchRecordOperations

Record operations on a BatchRequest.

BatchTableOperations

Table metadata operations on a BatchRequest.

BatchQueryOperations

Query operations on a BatchRequest.

BatchDataFrameOperations

DataFrame-oriented wrappers for batch record operations.

BatchRequest

Builder for constructing and executing a Dataverse OData $batch request.

BatchOperations

Namespace for batch operations (client.batch).

+
+
+

Module Contents

+
+
+class PowerPlatform.Dataverse.operations.batch.ChangeSetRecordOperations(cs_internal: PowerPlatform.Dataverse.data._batch._ChangeSet)
+

Record write operations available inside a ChangeSet.

+

Mirrors client.records but restricted to single-record forms (no bulk +create/update/delete). Only write operations are allowed — GET is not +permitted inside a changeset.

+

Do not instantiate directly; use ChangeSet.records.

+
+
+create(table: str, data: Dict[str, Any]) str
+

Add a single-record create to this changeset.

+
+
Parameters:
+
    +
  • table (str) – Table schema name (e.g. "account").

  • +
  • data (dict[str, Any]) – Column values for the new record.

  • +
+
+
Returns:
+

A content-ID reference string (e.g. "$1") usable in +subsequent operations within this changeset as a URI reference +in @odata.bind fields or as record_id in +update() / delete().

+
+
Return type:
+

str

+
+
+

Example:

+
with batch.changeset() as cs:
+    lead_ref = cs.records.create("lead", {"firstname": "Ada"})
+    cs.records.create("account", {
+        "name": "Babbage",
+        "originatingleadid@odata.bind": lead_ref,
+    })
+
+
+
+ +
+
+update(table: str, record_id: str, changes: Dict[str, Any]) None
+

Add a single-record update to this changeset.

+
+
Parameters:
+
    +
  • table (str) – Table schema name. Ignored when record_id is a +content-ID reference.

  • +
  • record_id (str) – GUID or a content-ID reference (e.g. "$1") +returned by a prior create() in this changeset.

  • +
  • changes (dict[str, Any]) – Column values to update.

  • +
+
+
+
+ +
+
+delete(table: str, record_id: str) None
+

Add a single-record delete to this changeset.

+
+
Parameters:
+
    +
  • table (str) – Table schema name. Ignored when record_id is a +content-ID reference.

  • +
  • record_id (str) – GUID or a content-ID reference (e.g. "$1").

  • +
+
+
+
+ +
+ +
+
+class PowerPlatform.Dataverse.operations.batch.ChangeSet(internal: PowerPlatform.Dataverse.data._batch._ChangeSet)
+

A transactional group of single-record write operations.

+

All operations succeed or are rolled back together. Use as a context +manager or call records to add operations directly.

+

Do not instantiate directly; use BatchRequest.changeset().

+

Example:

+
with batch.changeset() as cs:
+    ref = cs.records.create("contact", {"firstname": "Alice"})
+    cs.records.update("account", account_id, {
+        "primarycontactid@odata.bind": ref
+    })
+
+
+
+
+records
+
+ +
+ +
+
+class PowerPlatform.Dataverse.operations.batch.BatchRecordOperations(batch: _BatchContext)
+

Record operations on a BatchRequest.

+

Mirrors client.records: same method names, same signatures. +All methods return None; results are available via +BatchResult after +BatchRequest.execute().

+

GA methods: retrieve() (single record) and list() (multi-record, +single page). get() is deprecated — use retrieve() instead.

+

Do not instantiate directly; use batch.records.

+
+
+create(table: str, data: Dict[str, Any] | List[Dict[str, Any]]) None
+

Add a create operation to the batch.

+

A single dict creates one record (POST entity_set). +A list of dicts creates all records via the CreateMultiple action +(one batch item).

+
+
Parameters:
+
    +
  • table (str) – Table schema name (e.g. "account").

  • +
  • data (dict or list[dict]) – Single record dict or list of record dicts.

  • +
+
+
+
+ +
+
+update(table: str, ids: str | List[str], changes: Dict[str, Any] | List[Dict[str, Any]]) None
+

Add an update operation to the batch.

+
    +
  • Single (table, "guid", {...}) -> one PATCH request.

  • +
  • Broadcast (table, [id1, id2], {...}) -> one UpdateMultiple POST.

  • +
  • Paired (table, [id1, id2], [{...}, {...}]) -> one UpdateMultiple POST.

  • +
+
+
Parameters:
+
    +
  • table (str) – Table schema name.

  • +
  • ids (str or list[str]) – Single GUID or list of GUIDs.

  • +
  • changes (dict or list[dict]) – Single dict (single/broadcast) or list of dicts (paired).

  • +
+
+
+
+ +
+
+delete(table: str, ids: str | List[str], *, use_bulk_delete: bool = True) None
+

Add a delete operation to the batch.

+
    +
  • Single (table, "guid") -> one DELETE request.

  • +
  • List + use_bulk_delete=True (default) -> one BulkDelete POST. +The async job ID will be available in BatchItemResponse.data["JobId"].

  • +
  • List + use_bulk_delete=False -> one DELETE per record.

  • +
+
+
Parameters:
+
    +
  • table (str) – Table schema name.

  • +
  • ids (str or list[str]) – Single GUID or list of GUIDs.

  • +
  • use_bulk_delete (bool) – When True (default) and ids is a list, use the +BulkDelete action. When False, delete records individually.

  • +
+
+
+
+ +
+
+get(table: str, record_id: str, *, select: List[str] | None = None) None
+

Add a single-record get operation to the batch.

+
+

Deprecated since version Use: retrieve() instead. batch.records.get() is deprecated +and will be removed in a future release.

+
+
+
Parameters:
+
    +
  • table (str) – Table schema name.

  • +
  • record_id (str) – GUID of the record to retrieve.

  • +
  • select (list[str] or None) – Optional list of column names to include.

  • +
+
+
+
+ +
+
+upsert(table: str, items: List[PowerPlatform.Dataverse.models.upsert.UpsertItem | Dict[str, Any]]) None
+

Add an upsert operation to the batch.

+

Mirrors upsert(): +a single item becomes a PATCH request using the alternate key; multiple items +become one UpsertMultiple POST.

+

Each item must be a UpsertItem +or a plain dict with "alternate_key" and "record" keys (both dicts).

+
+
Parameters:
+
    +
  • table (str) – Table schema name (e.g. "account").

  • +
  • items (list[UpsertItem]) – Non-empty list of UpsertItem +instances or equivalent dicts.

  • +
+
+
Raises:
+

TypeError – If items is not a non-empty list, or if any element is +neither a UpsertItem nor a +dict with "alternate_key" and "record" keys.

+
+
+

Example:

+
from PowerPlatform.Dataverse.models import UpsertItem
+
+batch.records.upsert("account", [
+    UpsertItem(
+        alternate_key={"accountnumber": "ACC-001"},
+        record={"name": "Contoso Ltd"},
+    ),
+    UpsertItem(
+        alternate_key={"accountnumber": "ACC-002"},
+        record={"name": "Fabrikam Inc"},
+    ),
+])
+
+
+
+ +
+
+retrieve(table: str, record_id: str, *, select: List[str] | None = None, expand: List[str] | None = None, include_annotations: str | None = None) None
+

Add a single-record retrieve operation to the batch.

+

GA replacement for the deprecated get(). Enqueues a GET request +for one record by its GUID. The response body will be available in +data +after BatchRequest.execute().

+
+
Parameters:
+
    +
  • table (str) – Table schema name (e.g. "account").

  • +
  • record_id (str) – GUID of the record to retrieve.

  • +
  • select (list[str] or None) – Optional list of column logical names to include.

  • +
  • expand (list[str] or None) – Optional list of navigation properties to expand. +Navigation property names are case-sensitive and must match the +entity’s $metadata.

  • +
  • include_annotations (str or None) – OData annotation pattern for the +Prefer: odata.include-annotations header (e.g. "*" or +"OData.Community.Display.V1.FormattedValue"), or None.

  • +
+
+
+

Example:

+
batch = client.batch.new()
+batch.records.retrieve(
+    "account", account_id,
+    select=["name", "statuscode"],
+    expand=["primarycontactid"],
+    include_annotations="OData.Community.Display.V1.FormattedValue",
+)
+result = batch.execute()
+record = result.responses[0].data
+contact = (record.get("primarycontactid") or {})
+print(contact.get("fullname"))
+
+
+
+ +
+
+list(table: str, *, filter: str | FilterExpression | None = None, select: List[str] | None = None, orderby: List[str] | None = None, top: int | None = None, expand: List[str] | None = None, page_size: int | None = None, count: bool = False, include_annotations: str | None = None) None
+

Add a multi-record list operation to the batch (single page, no pagination).

+

Enqueues a GET request for multiple records. Because batch requests are +a single HTTP round-trip, pagination (@odata.nextLink) is not +supported — use top to bound the result size, or rely on the +server’s default page limit.

+

The response body ({"value": [...]} JSON) will be available in +data +after BatchRequest.execute().

+
+
Parameters:
+
    +
  • table (str) – Table schema name (e.g. "account").

  • +
  • filter (str or FilterExpression or None) – Optional OData $filter expression or FilterExpression.

  • +
  • select (list[str] or None) – Optional list of column logical names to include.

  • +
  • orderby (list[str] or None) – Optional list of sort expressions (e.g. ["name asc"]).

  • +
  • top (int or None) – Maximum number of records to return.

  • +
  • expand (list[str] or None) – Optional list of navigation properties to expand.

  • +
  • page_size (int or None) – Per-page size hint via Prefer: odata.maxpagesize.

  • +
  • count (bool) – If True, adds $count=true to the request.

  • +
  • include_annotations (str or None) – OData annotation pattern for the +Prefer: odata.include-annotations header, or None.

  • +
+
+
+

Example:

+
batch = client.batch.new()
+batch.records.list(
+    "account",
+    filter="statecode eq 0",
+    select=["name", "statuscode"],
+    orderby=["name asc"],
+    top=50,
+    include_annotations="OData.Community.Display.V1.FormattedValue",
+)
+result = batch.execute()
+records = result.responses[0].data.get("value", [])
+
+
+
+ +
+ +
+
+class PowerPlatform.Dataverse.operations.batch.BatchTableOperations(batch: _BatchContext)
+

Table metadata operations on a BatchRequest.

+

Mirrors client.tables exactly: same method names, same signatures. +All methods return None; results arrive via +BatchResult.

+
+

Note

+

tables.delete, tables.add_columns, and tables.remove_columns +require a metadata lookup (GET EntityDefinitions) at +BatchRequest.execute() time to resolve the table’s MetadataId. +This lookup is transparent to the caller.

+
+
+

Note

+

tables.add_columns and tables.remove_columns each produce one +batch item per column, so they contribute multiple entries to +responses.

+
+

Do not instantiate directly; use batch.tables.

+
+
+create(table: str, columns: Dict[str, Any], *, solution: str | None = None, primary_column: str | None = None, display_name: str | None = None) None
+

Add a table-create operation to the batch.

+
+

Note

+

The pre-existence check performed by client.tables.create is skipped +in batch mode. If the table already exists the server returns an error +in the corresponding BatchItemResponse.

+
+
+
Parameters:
+
    +
  • table (str) – Schema name of the new table (e.g. "new_Product").

  • +
  • columns (dict[str, Any]) – Mapping of column schema names to type strings or Enum subclasses.

  • +
  • solution (str or None) – Optional solution unique name.

  • +
  • primary_column (str or None) – Optional primary column schema name.

  • +
  • display_name (str or None) – Human-readable display name for the table. +When omitted, defaults to the table schema name.

  • +
+
+
+
+ +
+
+delete(table: str) None
+

Add a table-delete operation to the batch.

+

The table’s MetadataId is resolved via a GET request at execute time.

+
+
Parameters:
+

table (str) – Schema name of the table to delete.

+
+
+
+ +
+
+get(table: str) None
+

Add a table-metadata-get operation to the batch.

+

The response will be in BatchItemResponse.data after execute.

+
+
Parameters:
+

table (str) – Schema name of the table.

+
+
+
+ +
+
+list(*, filter: str | None = None, select: List[str] | None = None) None
+

Add a list-all-tables operation to the batch.

+

Mirrors client.tables.list(). Supply an optional OData +$filter expression to further narrow the results (combined with +IsPrivate eq false using and). select projects +specific property names via $select.

+

The response will be in BatchItemResponse.data after execute.

+
+
Parameters:
+
    +
  • filter (str or None) – Additional OData $filter expression.

  • +
  • select (list[str] or None) – List of property names for $select.

  • +
+
+
+
+ +
+
+add_columns(table: str, columns: Dict[str, Any]) None
+

Add column-create operations to the batch (one per column).

+

The table’s MetadataId is resolved at execute time. Each column +produces one entry in responses.

+
+
Parameters:
+
    +
  • table (str) – Schema name of the target table.

  • +
  • columns (dict[str, Any]) – Mapping of column schema names to type strings or Enum subclasses.

  • +
+
+
+
+ +
+
+remove_columns(table: str, columns: str | List[str]) None
+

Add column-delete operations to the batch (one per column).

+

The table’s MetadataId and each column’s MetadataId are resolved +at execute time. Each column produces one entry in +responses.

+
+
Parameters:
+
    +
  • table (str) – Schema name of the target table.

  • +
  • columns (str or list[str]) – Column schema name or list of column schema names to remove.

  • +
+
+
+
+ +
+
+create_one_to_many_relationship(lookup: PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata, relationship: PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata, *, solution: str | None = None) None
+

Add a one-to-many relationship creation to the batch.

+
+
Parameters:
+
+
+
+
+ +
+
+create_many_to_many_relationship(relationship: PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata, *, solution: str | None = None) None
+

Add a many-to-many relationship creation to the batch.

+
+
Parameters:
+
+
+
+
+ +
+
+delete_relationship(relationship_id: str) None
+

Add a relationship-delete operation to the batch.

+
+
Parameters:
+

relationship_id (str) – GUID of the relationship metadata to delete.

+
+
+
+ +
+
+get_relationship(schema_name: str) None
+

Add a relationship-metadata-get operation to the batch.

+

The response will be in BatchItemResponse.data after execute.

+
+
Parameters:
+

schema_name (str) – Schema name of the relationship.

+
+
+
+ +
+
+create_lookup_field(referencing_table: str, lookup_field_name: str, referenced_table: str, *, display_name: str | None = None, description: str | None = None, required: bool = False, cascade_delete: str = CASCADE_BEHAVIOR_REMOVE_LINK, solution: str | None = None, language_code: int = 1033) None
+

Add a lookup field creation to the batch (convenience wrapper for +create_one_to_many_relationship()).

+
+
Parameters:
+
    +
  • referencing_table (str) – Logical name of the child (many) table.

  • +
  • lookup_field_name (str) – Schema name for the lookup field.

  • +
  • referenced_table (str) – Logical name of the parent (one) table.

  • +
  • display_name (str or None) – Display name for the lookup field.

  • +
  • description (str or None) – Optional description.

  • +
  • required (bool) – Whether the lookup is required.

  • +
  • cascade_delete (str) – Delete cascade behaviour.

  • +
  • solution (str or None) – Optional solution unique name.

  • +
  • language_code (int) – Language code for labels (default 1033).

  • +
+
+
+
+ +
+ +
+
+class PowerPlatform.Dataverse.operations.batch.BatchQueryOperations(batch: _BatchContext)
+

Query operations on a BatchRequest.

+

Mirrors client.query exactly: same method names, same signatures. +All methods return None; results arrive via +BatchResult.

+

Do not instantiate directly; use batch.query.

+
+
+sql(sql: str) None
+

Add a SQL SELECT query to the batch.

+

Mirrors sql(). +The entity set is resolved from the table name in the SQL statement at +BatchRequest.execute() time.

+
+
Parameters:
+

sql (str) – A single SELECT statement within the Dataverse-supported subset.

+
+
Raises:
+

ValidationError – If sql is not a non-empty string.

+
+
+

Example:

+
batch.query.sql("SELECT accountid, name FROM account WHERE name = 'Contoso'")
+
+
+
+ +
+ +
+
+class PowerPlatform.Dataverse.operations.batch.BatchDataFrameOperations(batch: _BatchContext)
+

DataFrame-oriented wrappers for batch record operations.

+

Provides create(), update(), and delete() that accept +pandas.DataFrame / pandas.Series inputs and convert them to standard +dicts before enqueueing on the batch. This lets data-science callers feed +DataFrames directly into a batch without manual conversion.

+

Accessed via batch.dataframe.

+

Example:

+
import pandas as pd
+
+batch = client.batch.new()
+df = pd.DataFrame([
+    {"name": "Contoso", "telephone1": "555-0100"},
+    {"name": "Fabrikam", "telephone1": "555-0200"},
+])
+batch.dataframe.create("account", df)
+result = batch.execute()
+
+
+
+
+create(table: str, records: pandas.DataFrame) None
+

Enqueue record creates from a pandas DataFrame.

+

Each row becomes a record. All rows are bundled in a single +CreateMultiple batch item (one HTTP request in the batch).

+
+
Parameters:
+
    +
  • table (str) – Table schema name (e.g. "account").

  • +
  • records (DataFrame) – DataFrame where each row is a record to create.

  • +
+
+
Raises:
+
    +
  • TypeError – If records is not a pandas DataFrame.

  • +
  • ValueError – If records is empty or any row has no non-null values.

  • +
+
+
+

Example:

+
df = pd.DataFrame([{"name": "Contoso"}, {"name": "Fabrikam"}])
+batch.dataframe.create("account", df)
+
+
+
+ +
+
+update(table: str, changes: pandas.DataFrame, id_column: str, clear_nulls: bool = False) None
+

Enqueue record updates from a pandas DataFrame.

+

Each row represents an update. The id_column specifies which +column contains the record GUIDs.

+
+
Parameters:
+
    +
  • table (str) – Table schema name (e.g. "account").

  • +
  • changes (DataFrame) – DataFrame where each row contains a record GUID and +the fields to update.

  • +
  • id_column (str) – Name of the DataFrame column containing record GUIDs.

  • +
  • clear_nulls (bool) – When False (default), NaN/None values are +skipped. When True, NaN/None sends null to clear the field.

  • +
+
+
Raises:
+
    +
  • TypeError – If changes is not a pandas DataFrame.

  • +
  • ValueError – If changes is empty, id_column is missing, +or IDs are invalid.

  • +
+
+
+

Example:

+
df = pd.DataFrame([
+    {"accountid": "guid-1", "telephone1": "555-0100"},
+    {"accountid": "guid-2", "telephone1": "555-0200"},
+])
+batch.dataframe.update("account", df, id_column="accountid")
+
+
+
+ +
+
+delete(table: str, ids: pandas.Series, use_bulk_delete: bool = True) None
+

Enqueue record deletes from a pandas Series of GUIDs.

+
+
Parameters:
+
    +
  • table (str) – Table schema name (e.g. "account").

  • +
  • ids (Series) – Series of record GUIDs to delete.

  • +
  • use_bulk_delete (bool) – When True (default) and ids has multiple +values, use the BulkDelete action.

  • +
+
+
Raises:
+
    +
  • TypeError – If ids is not a pandas Series.

  • +
  • ValueError – If ids contains invalid values.

  • +
+
+
+

Example:

+
ids_series = pd.Series(["guid-1", "guid-2", "guid-3"])
+batch.dataframe.delete("account", ids_series)
+
+
+
+ +
+ +
+
+class PowerPlatform.Dataverse.operations.batch.BatchRequest(client: PowerPlatform.Dataverse.client.DataverseClient)
+

Builder for constructing and executing a Dataverse OData $batch request.

+

Obtain via BatchOperations.new() (client.batch.new()). Add operations +through records, tables, query, and dataframe, +optionally group writes +into a changeset(), then call execute().

+

Operations are executed sequentially in the order added. The resulting +BatchResult contains one +BatchItemResponse per HTTP +request dispatched (some operations expand to multiple requests).

+
+

Note

+

Maximum 1000 HTTP operations per batch.

+
+

Example:

+
batch = client.batch.new()
+batch.records.create("account", {"name": "Contoso"})
+batch.tables.get("account")
+with batch.changeset() as cs:
+    ref = cs.records.create("contact", {"firstname": "Alice"})
+    cs.records.update("account", account_id, {
+        "primarycontactid@odata.bind": ref
+    })
+result = batch.execute()
+
+
+
+
+records
+
+ +
+
+tables
+
+ +
+
+query
+
+ +
+
+dataframe
+
+ +
+
+changeset() ChangeSet
+

Create a new ChangeSet attached to this batch.

+

The changeset is added to the batch immediately. Operations added to +the returned ChangeSet via cs.records.* execute atomically.

+
+
Returns:
+

A new ChangeSet ready to receive operations.

+
+
+

Example:

+
with batch.changeset() as cs:
+    cs.records.create("account", {"name": "ACME"})
+    cs.records.create("contact", {"firstname": "Bob"})
+
+
+
+ +
+
+execute(*, continue_on_error: bool = False) PowerPlatform.Dataverse.models.batch.BatchResult
+

Submit the batch to Dataverse and return all responses.

+
+
Parameters:
+

continue_on_error – When False (default), Dataverse stops at the +first failure and returns that operation’s error as a 4xx response. +When True, Prefer: odata.continue-on-error is sent and all +operations are attempted.

+
+
Returns:
+

BatchResult +with one entry per HTTP operation in submission order.

+
+
Raises:
+
    +
  • ValidationError – If the batch exceeds 1000 operations or an +unsupported column type is specified.

  • +
  • MetadataError – If metadata pre-resolution fails (table or +column not found) for tables.delete, tables.add_columns, +or tables.remove_columns.

  • +
  • HttpError – On HTTP-level failures (auth, server error, etc.) +that prevent the batch from executing.

  • +
+
+
+
+ +
+ +
+
+class PowerPlatform.Dataverse.operations.batch.BatchOperations(client: PowerPlatform.Dataverse.client.DataverseClient)
+

Namespace for batch operations (client.batch).

+

Accessed via client.batch. Use new() to create a +BatchRequest builder.

+
+
Parameters:
+

client – The parent DataverseClient instance.

+
+
+

Example:

+
batch = client.batch.new()
+batch.records.create("account", {"name": "Fabrikam"})
+result = batch.execute()
+
+
+
+
+new() BatchRequest
+

Create a new empty BatchRequest builder.

+
+
Returns:
+

An empty BatchRequest.

+
+
+
+ +
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/dataframe/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/dataframe/index.html new file mode 100644 index 00000000..2a63021b --- /dev/null +++ b/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/dataframe/index.html @@ -0,0 +1,413 @@ + + + + + + + + PowerPlatform.Dataverse.operations.dataframe — PowerPlatform-Dataverse-Client 0.1.0b11 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

PowerPlatform.Dataverse.operations.dataframe

+

DataFrame CRUD operations namespace for the Dataverse SDK.

+
+

Classes

+ + + + + + +

DataFrameOperations

Namespace for pandas DataFrame CRUD operations.

+
+
+

Module Contents

+
+
+class PowerPlatform.Dataverse.operations.dataframe.DataFrameOperations(client: PowerPlatform.Dataverse.client.DataverseClient)
+

Namespace for pandas DataFrame CRUD operations.

+

Accessed via client.dataframe. Provides DataFrame-oriented wrappers +around the record-level CRUD operations.

+
+
Parameters:
+

client (DataverseClient) – The parent DataverseClient instance.

+
+
+

Example:

+
import pandas as pd
+
+client = DataverseClient(base_url, credential)
+
+# Query records as a DataFrame
+df = client.dataframe.get("account", select=["name"], top=100)
+
+# Create records from a DataFrame
+new_df = pd.DataFrame([{"name": "Contoso"}, {"name": "Fabrikam"}])
+new_df["accountid"] = client.dataframe.create("account", new_df)
+
+# Update records
+new_df["telephone1"] = ["555-0100", "555-0200"]
+client.dataframe.update("account", new_df, id_column="accountid")
+
+# Delete records
+client.dataframe.delete("account", new_df["accountid"])
+
+
+
+
+sql(sql: str) pandas.DataFrame
+

Execute a SQL query and return the results as a pandas DataFrame.

+

Delegates to sql() +and converts the list of records into a single DataFrame.

+
+
Parameters:
+

sql (str) – Supported SQL SELECT statement.

+
+
Returns:
+

DataFrame containing all result rows. Returns an empty +DataFrame when no rows match.

+
+
Return type:
+

DataFrame

+
+
Raises:
+

ValidationError – If sql is not a string or is empty.

+
+
+

Example

+

SQL query to DataFrame:

+
df = client.dataframe.sql(
+    "SELECT TOP 100 name, revenue FROM account "
+    "WHERE statecode = 0 ORDER BY revenue"
+)
+print(f"Got {len(df)} rows")
+print(df.head())
+
+
+

Aggregate query to DataFrame:

+
df = client.dataframe.sql(
+    "SELECT a.name, COUNT(c.contactid) as cnt "
+    "FROM account a "
+    "JOIN contact c ON a.accountid = c.parentcustomerid "
+    "GROUP BY a.name"
+)
+
+
+
+ +
+
+get(table: str, record_id: str | None = None, select: List[str] | None = None, filter: str | None = None, orderby: List[str] | None = None, top: int | None = None, expand: List[str] | None = None, page_size: int | None = None, count: bool = False, include_annotations: str | None = None) pandas.DataFrame
+

Fetch records and return as a single pandas DataFrame.

+

When record_id is provided, returns a single-row DataFrame. +When record_id is None, internally iterates all pages and returns one +consolidated DataFrame.

+
+
Parameters:
+
    +
  • table (str) – Schema name of the table (e.g. "account" or "new_MyTestTable").

  • +
  • record_id (str or None) – Optional GUID to fetch a specific record. If None, queries multiple records.

  • +
  • select (list[str] or None) – Optional list of attribute logical names to retrieve.

  • +
  • filter (str or None) – Optional OData filter string. Column names must use exact lowercase logical names.

  • +
  • orderby (list[str] or None) – Optional list of attributes to sort by.

  • +
  • top (int or None) – Optional maximum number of records to return.

  • +
  • expand (list[str] or None) – Optional list of navigation properties to expand (case-sensitive).

  • +
  • page_size (int or None) – Optional number of records per page for pagination.

  • +
  • count (bool) – If True, adds $count=true to include a total +record count in the response.

  • +
  • include_annotations (str or None) – OData annotation pattern for the +Prefer: odata.include-annotations header (e.g. "*" or +"OData.Community.Display.V1.FormattedValue"), or None.

  • +
+
+
Returns:
+

DataFrame containing all matching records. Returns an empty DataFrame +when no records match.

+
+
Return type:
+

DataFrame

+
+
Raises:
+

ValueError – If record_id is not a non-empty string, or if +query parameters (filter, orderby, top, expand, +page_size) are provided alongside record_id.

+
+
+
+

Tip

+

For large tables, use top or filter to limit the result set.

+
+

Example

+

Fetch a single record as a DataFrame:

+
df = client.dataframe.get("account", record_id=account_id, select=["name", "telephone1"])
+print(df)
+
+
+

Query with filtering:

+
df = client.dataframe.get("account", filter="statecode eq 0", select=["name"])
+print(f"Got {len(df)} active accounts")
+
+
+

Limit result size:

+
df = client.dataframe.get("account", select=["name"], top=100)
+
+
+
+ +
+
+create(table: str, records: pandas.DataFrame) pandas.Series
+

Create records from a pandas DataFrame.

+
+
Parameters:
+
    +
  • table (str) – Schema name of the table (e.g. "account" or "new_MyTestTable").

  • +
  • records (DataFrame) – DataFrame where each row is a record to create.

  • +
+
+
Returns:
+

Series of created record GUIDs, aligned with the input DataFrame index.

+
+
Return type:
+

Series

+
+
Raises:
+
    +
  • TypeError – If records is not a pandas DataFrame.

  • +
  • ValueError – If records is empty or the number of returned +IDs does not match the number of input rows.

  • +
+
+
+
+

Tip

+

All rows are sent in a single CreateMultiple request. For very +large DataFrames, consider splitting into smaller batches to avoid +request timeouts.

+
+

Example

+

Create records from a DataFrame:

+
import pandas as pd
+
+df = pd.DataFrame([
+    {"name": "Contoso", "telephone1": "555-0100"},
+    {"name": "Fabrikam", "telephone1": "555-0200"},
+])
+df["accountid"] = client.dataframe.create("account", df)
+
+
+
+ +
+
+update(table: str, changes: pandas.DataFrame, id_column: str, clear_nulls: bool = False) None
+

Update records from a pandas DataFrame.

+

Each row in the DataFrame represents an update. The id_column specifies which +column contains the record GUIDs.

+
+
Parameters:
+
    +
  • table (str) – Schema name of the table (e.g. "account" or "new_MyTestTable").

  • +
  • changes (DataFrame) – DataFrame where each row contains a record GUID and the fields to update.

  • +
  • id_column (str) – Name of the DataFrame column containing record GUIDs.

  • +
  • clear_nulls (bool) – When False (default), missing values (NaN/None) are skipped +(the field is left unchanged on the server). When True, missing values are sent +as null to Dataverse, clearing the field. Use True only when you intentionally +want NaN/None values to clear fields.

  • +
+
+
Raises:
+
    +
  • TypeError – If changes is not a pandas DataFrame.

  • +
  • ValueError – If changes is empty, id_column is not found in the +DataFrame, id_column contains invalid (non-string, empty, or whitespace-only) +values, or no updatable columns exist besides id_column. +When clear_nulls is False (default), rows where all change values +are NaN/None produce empty patches and are silently skipped. If all rows +are skipped, the method returns without making an API call. When +clear_nulls is True, NaN/None values become explicit nulls, so +rows are never skipped.

  • +
+
+
+
+

Tip

+

All rows are sent in a single UpdateMultiple request (or a +single PATCH for one row). For very large DataFrames, consider +splitting into smaller batches to avoid request timeouts.

+
+

Example

+

Update records with different values per row:

+
import pandas as pd
+
+df = pd.DataFrame([
+    {"accountid": "guid-1", "telephone1": "555-0100"},
+    {"accountid": "guid-2", "telephone1": "555-0200"},
+])
+client.dataframe.update("account", df, id_column="accountid")
+
+
+

Broadcast the same change to all records:

+
df = pd.DataFrame({"accountid": ["guid-1", "guid-2", "guid-3"]})
+df["websiteurl"] = "https://example.com"
+client.dataframe.update("account", df, id_column="accountid")
+
+
+

Clear a field by setting clear_nulls=True:

+
df = pd.DataFrame([{"accountid": "guid-1", "websiteurl": None}])
+client.dataframe.update("account", df, id_column="accountid", clear_nulls=True)
+
+
+
+ +
+
+delete(table: str, ids: pandas.Series, use_bulk_delete: bool = True) str | None
+

Delete records by passing a pandas Series of GUIDs.

+
+
Parameters:
+
    +
  • table (str) – Schema name of the table (e.g. "account" or "new_MyTestTable").

  • +
  • ids (Series) – Series of record GUIDs to delete.

  • +
  • use_bulk_delete (bool) – When True (default) and ids contains multiple values, execute the BulkDelete +action and return its async job identifier. When False each record is deleted sequentially.

  • +
+
+
Raises:
+
    +
  • TypeError – If ids is not a pandas Series.

  • +
  • ValueError – If ids contains invalid (non-string, empty, or +whitespace-only) values.

  • +
+
+
Returns:
+

BulkDelete job ID when deleting multiple records via BulkDelete; +None when deleting a single record, using sequential deletion, or +when ids is empty.

+
+
Return type:
+

str or None

+
+
+

Example

+

Delete records using a Series:

+
import pandas as pd
+
+ids = pd.Series(["guid-1", "guid-2", "guid-3"])
+client.dataframe.delete("account", ids)
+
+
+
+ +
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/files/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/files/index.html new file mode 100644 index 00000000..de856206 --- /dev/null +++ b/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/files/index.html @@ -0,0 +1,212 @@ + + + + + + + + PowerPlatform.Dataverse.operations.files — PowerPlatform-Dataverse-Client 0.1.0b11 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

PowerPlatform.Dataverse.operations.files

+

File operations namespace for the Dataverse SDK.

+
+

Classes

+ + + + + + +

FileOperations

Namespace for file operations.

+
+
+

Module Contents

+
+
+class PowerPlatform.Dataverse.operations.files.FileOperations(client: PowerPlatform.Dataverse.client.DataverseClient)
+

Namespace for file operations.

+

Accessed via client.files. Provides file upload operations for +Dataverse file columns.

+
+
Parameters:
+

client (DataverseClient) – The parent DataverseClient instance.

+
+
+

Example:

+
client = DataverseClient(base_url, credential)
+
+client.files.upload(
+    "account", account_id, "new_Document", "/path/to/file.pdf"
+)
+
+
+
+
+upload(table: str, record_id: str, file_column: str, path: str, *, mode: str | None = None, mime_type: str | None = None, if_none_match: bool = True) None
+

Upload a file to a Dataverse file column.

+
+
Parameters:
+
    +
  • table (str) – Schema name of the table (e.g. "account" or +"new_MyTestTable").

  • +
  • record_id (str) – GUID of the target record.

  • +
  • file_column (str) – Schema name of the file column attribute (e.g., +"new_Document"). If the column doesn’t exist, it will be +created automatically.

  • +
  • path (str) – Local filesystem path to the file. The stored filename +will be the basename of this path.

  • +
  • mode (str or None) – Upload strategy: "auto" (default), "small", or +"chunk". Auto mode selects small or chunked upload based on +file size.

  • +
  • mime_type (str or None) – Explicit MIME type to store with the file (e.g. +"application/pdf"). If not provided, defaults to +"application/octet-stream".

  • +
  • if_none_match (bool) – When True (default), sends +If-None-Match: null header to only succeed if the column is +currently empty. Set False to always overwrite using +If-Match: *.

  • +
+
+
Raises:
+
    +
  • HttpError – If the upload fails or the file column is not empty when +if_none_match=True.

  • +
  • FileNotFoundError – If the specified file path does not exist.

  • +
+
+
+

Example

+

Upload a PDF file:

+
client.files.upload(
+    "account",
+    account_id,
+    "new_Contract",
+    "/path/to/contract.pdf",
+    mime_type="application/pdf",
+)
+
+
+

Upload with auto mode selection:

+
client.files.upload(
+    "email",
+    email_id,
+    "new_Attachment",
+    "/path/to/large_file.zip",
+)
+
+
+
+ +
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/index.html new file mode 100644 index 00000000..238598f2 --- /dev/null +++ b/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/index.html @@ -0,0 +1,134 @@ + + + + + + + + PowerPlatform.Dataverse.operations — PowerPlatform-Dataverse-Client 0.1.0b11 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

PowerPlatform.Dataverse.operations

+

Operation namespaces for the Dataverse SDK.

+

This module contains the operation namespace classes that organize +SDK operations into logical groups: records, query, and tables.

+
+

Submodules

+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/query/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/query/index.html new file mode 100644 index 00000000..05372f0d --- /dev/null +++ b/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/query/index.html @@ -0,0 +1,497 @@ + + + + + + + + PowerPlatform.Dataverse.operations.query — PowerPlatform-Dataverse-Client 0.1.0b11 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

PowerPlatform.Dataverse.operations.query

+

Query operations namespace for the Dataverse SDK.

+
+

Classes

+ + + + + + +

QueryOperations

Namespace for query operations.

+
+
+

Module Contents

+
+
+class PowerPlatform.Dataverse.operations.query.QueryOperations(client: PowerPlatform.Dataverse.client.DataverseClient)
+

Namespace for query operations.

+

Accessed via client.query. Provides query and search operations +against Dataverse tables.

+
+
Parameters:
+

client (DataverseClient) – The parent DataverseClient instance.

+
+
+

Example:

+
from PowerPlatform.Dataverse.models.filters import col
+
+client = DataverseClient(base_url, credential)
+
+# Fluent query builder (recommended)
+for record in (client.query.builder("account")
+               .select("name", "revenue")
+               .where(col("statecode") == 0)
+               .order_by("revenue", descending=True)
+               .top(100)
+               .execute()):
+    print(record["name"])
+
+# SQL query
+rows = client.query.sql("SELECT TOP 10 name FROM account ORDER BY name")
+for row in rows:
+    print(row["name"])
+
+
+
+
+builder(table: str) PowerPlatform.Dataverse.models.query_builder.QueryBuilder
+

Create a fluent query builder for the specified table.

+

Returns a QueryBuilder +that can be chained with filter, select, and order methods, then +executed directly via .execute().

+
+
Parameters:
+

table (str) – Table schema name (e.g. "account").

+
+
Returns:
+

A QueryBuilder instance bound to this client.

+
+
Return type:
+

QueryBuilder

+
+
+

Example

+

Build and execute a query fluently:

+
from PowerPlatform.Dataverse.models.filters import col
+
+for record in (client.query.builder("account")
+               .select("name", "revenue")
+               .where(col("statecode") == 0)
+               .where(col("revenue") > 1_000_000)
+               .order_by("revenue", descending=True)
+               .top(100)
+               .page_size(50)
+               .execute()):
+    print(record["name"])
+
+
+

With composable expression tree:

+
from PowerPlatform.Dataverse.models.filters import col
+
+for record in (client.query.builder("account")
+               .where((col("statecode") == 0) | (col("statecode") == 1))
+               .where(col("revenue") > 100_000)
+               .execute()):
+    print(record["name"])
+
+
+
+ +
+
+sql(sql: str) List[PowerPlatform.Dataverse.models.record.Record]
+

Execute a read-only SQL query using the Dataverse Web API.

+

The Dataverse SQL endpoint supports a broad subset of T-SQL:

+
SELECT / SELECT DISTINCT / SELECT TOP N (0-5000)
+FROM table [alias]
+INNER JOIN / LEFT JOIN (multi-table, no depth limit)
+WHERE (=, !=, >, <, >=, <=, LIKE, IN, NOT IN, IS NULL,
+       IS NOT NULL, BETWEEN, AND, OR, nested parentheses)
+GROUP BY column
+ORDER BY column [ASC|DESC]
+OFFSET n ROWS FETCH NEXT m ROWS ONLY
+COUNT(*), SUM(), AVG(), MIN(), MAX()
+
+
+

SELECT * is not supported – specify column names explicitly. +Use sql_columns() to discover available column names for a table.

+

Not supported: SELECT *, subqueries, CTE, HAVING, UNION, +RIGHT/FULL/CROSS JOIN, CASE, COALESCE, window functions, +string/date/math functions, INSERT/UPDATE/DELETE. For writes, use +client.records methods.

+
+
Parameters:
+

sql (str) – Supported SQL SELECT statement.

+
+
Returns:
+

List of Record +objects. Returns an empty list when no rows match.

+
+
Return type:
+

list[Record]

+
+
Raises:
+

ValidationError – If sql is not a string or is empty.

+
+
+

Example

+

Basic query:

+
rows = client.query.sql(
+    "SELECT TOP 10 name FROM account ORDER BY name"
+)
+
+
+

JOIN with aggregation:

+
rows = client.query.sql(
+    "SELECT a.name, COUNT(c.contactid) as cnt "
+    "FROM account a "
+    "JOIN contact c ON a.accountid = c.parentcustomerid "
+    "GROUP BY a.name"
+)
+
+
+
+ +
+
+fetchxml(xml: str) PowerPlatform.Dataverse.models.fetchxml_query.FetchXmlQuery
+

Return an inert FetchXmlQuery object.

+

No HTTP request is made until +execute() +or +execute_pages() +is called on the returned object.

+

Use for SQL-JOIN scenarios, aggregate queries, or other operations that +the OData builder endpoint cannot express.

+
+
Parameters:
+

xml (str) – Well-formed FetchXML query string. The root <entity name="..."> +element determines the entity set endpoint.

+
+
Returns:
+

Inert query object with .execute() and .execute_pages() methods.

+
+
Return type:
+

FetchXmlQuery

+
+
Raises:
+

ValueError – If the FetchXML is missing a root <entity> element +or the entity name attribute.

+
+
+

Example:

+
query = client.query.fetchxml("""
+  <fetch top="50">
+    <entity name="account">
+      <attribute name="name" />
+      <link-entity name="contact" from="parentcustomerid"
+                   to="accountid" alias="c" link-type="inner">
+        <attribute name="fullname" />
+      </link-entity>
+    </entity>
+  </fetch>
+""")
+
+# Eager — collect all pages:
+result = query.execute()
+df = result.to_dataframe()
+
+# Lazy — process one page at a time:
+for page in query.execute_pages():
+    process(page.to_dataframe())
+
+
+
+ +
+
+sql_columns(table: str, *, include_system: bool = False) List[Dict[str, Any]]
+

Return a simplified list of SQL-usable columns for a table.

+

Each dict contains name (logical name for SQL), type +(Dataverse attribute type), is_pk (primary key flag), and +label (display name). Virtual columns are always excluded +because the SQL endpoint cannot query them.

+
+
Parameters:
+
    +
  • table (str) – Schema name of the table (e.g. "account").

  • +
  • include_system (bool) – When False (default), columns that end +with common system suffixes (_base, versionnumber, +timezoneruleversionnumber, utcconversiontimezonecode, +importsequencenumber, overriddencreatedon) are excluded.

  • +
+
+
Returns:
+

List of column metadata dicts.

+
+
Return type:
+

list[dict[str, Any]]

+
+
+

Example:

+
cols = client.query.sql_columns("account")
+for c in cols:
+    print(f"{c['name']:30s} {c['type']:20s} PK={c['is_pk']}")
+
+
+
+ +
+
+odata_select(table: str, *, include_system: bool = False) List[str]
+

Return a list of column logical names suitable for $select.

+

Can be passed directly to client.records.get(table, select=...).

+
+
Parameters:
+
    +
  • table (str) – Schema name of the table (e.g. "account").

  • +
  • include_system (bool) – Include system columns (default False).

  • +
+
+
Returns:
+

List of lowercase column logical names.

+
+
Return type:
+

list[str]

+
+
+

Example:

+
cols = client.query.odata_select("account")
+for page in client.records.get("account", select=cols, top=10):
+    for r in page:
+        print(r)
+
+
+
+ +
+
+odata_expands(table: str) List[Dict[str, Any]]
+

Discover all $expand navigation properties from a table.

+

Returns entries for each outgoing lookup (single-valued navigation +property). Each entry contains the exact PascalCase navigation +property name needed for $expand and @odata.bind, plus +the target entity set name.

+
+
Parameters:
+

table (str) – Schema name of the table (e.g. "contact").

+
+
Returns:
+

List of dicts, each with:

+
    +
  • nav_property – PascalCase navigation property for $expand

  • +
  • target_table – target entity logical name

  • +
  • target_entity_set – target entity set (for @odata.bind)

  • +
  • lookup_attribute – the lookup column logical name

  • +
  • relationship – relationship schema name

  • +
+

+
+
Return type:
+

list[dict[str, Any]]

+
+
+

Example:

+
expands = client.query.odata_expands("contact")
+for e in expands:
+    print(f"expand={e['nav_property']}  -> {e['target_table']}")
+
+# Use in a query
+e = next(e for e in expands if e['target_table'] == 'account')
+for page in client.records.get("contact",
+                               select=["fullname"],
+                               expand=[e['nav_property']]):
+    ...
+
+
+
+ +
+
+odata_expand(from_table: str, to_table: str) str
+

Return the navigation property name to $expand from one table to another.

+

Discovers via relationship metadata. Returns the exact PascalCase +string for the expand= parameter.

+
+
Parameters:
+
    +
  • from_table (str) – Schema name of the source table (e.g. "contact").

  • +
  • to_table (str) – Schema name of the target table (e.g. "account").

  • +
+
+
Returns:
+

The navigation property name (PascalCase).

+
+
Return type:
+

str

+
+
Raises:
+

ValueError – If no navigation property found for the target.

+
+
+

Example:

+
nav = client.query.odata_expand("contact", "account")
+# Returns e.g. "parentcustomerid_account"
+for page in client.records.get("contact",
+                               select=["fullname"],
+                               expand=[nav],
+                               top=5):
+    for r in page:
+        acct = r.get(nav) or {}
+        print(f"{r['fullname']} -> {acct.get('name', 'N/A')}")
+
+
+
+ +
+
+odata_bind(from_table: str, to_table: str, target_id: str) Dict[str, str]
+

Build an @odata.bind entry for setting a lookup field.

+

Auto-discovers the navigation property name and entity set name +from metadata. Returns a single-entry dict that can be merged +into a create or update payload.

+
+
Parameters:
+
    +
  • from_table (str) – Schema name of the entity being created/updated.

  • +
  • to_table (str) – Schema name of the target entity the lookup points to.

  • +
  • target_id (str) – GUID of the target record.

  • +
+
+
Returns:
+

A dict like {"NavProp@odata.bind": "/entityset(guid)"}.

+
+
Return type:
+

dict[str, str]

+
+
Raises:
+

ValueError – If no relationship found between the tables.

+
+
+

Example:

+
# Instead of manually constructing:
+#   {"parentcustomerid_account@odata.bind": "/accounts(guid)"}
+# Just do:
+bind = client.query.odata_bind("contact", "account", acct_id)
+client.records.create("contact", {
+    "firstname": "Jane",
+    "lastname": "Doe",
+    **bind,
+})
+
+
+
+ +
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/records/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/records/index.html new file mode 100644 index 00000000..5c185faf --- /dev/null +++ b/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/records/index.html @@ -0,0 +1,588 @@ + + + + + + + + PowerPlatform.Dataverse.operations.records — PowerPlatform-Dataverse-Client 0.1.0b11 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

PowerPlatform.Dataverse.operations.records

+

Record CRUD operations namespace for the Dataverse SDK.

+
+

Classes

+ + + + + + +

RecordOperations

Namespace for record-level CRUD operations.

+
+
+

Module Contents

+
+
+class PowerPlatform.Dataverse.operations.records.RecordOperations(client: PowerPlatform.Dataverse.client.DataverseClient)
+

Namespace for record-level CRUD operations.

+

Accessed via client.records. Provides create, update, delete, and get +operations on individual Dataverse records.

+
+
Parameters:
+

client (DataverseClient) – The parent DataverseClient instance.

+
+
+

Example:

+
client = DataverseClient(base_url, credential)
+
+# Create a single record
+guid = client.records.create("account", {"name": "Contoso Ltd"})
+
+# Get a record
+record = client.records.get("account", guid, select=["name"])
+
+# Update a record
+client.records.update("account", guid, {"telephone1": "555-0100"})
+
+# Delete a record
+client.records.delete("account", guid)
+
+
+
+
+create(table: str, data: Dict[str, Any]) str
+
+create(table: str, data: List[Dict[str, Any]]) List[str]
+

Create one or more records in a Dataverse table.

+

When data is a single dictionary, creates one record and returns its +GUID as a string. When data is a list of dictionaries, creates all +records via the CreateMultiple action and returns a list of GUIDs.

+
+
Parameters:
+
    +
  • table (str) – Schema name of the table (e.g. "account" or "new_MyTestTable").

  • +
  • data (dict or list[dict]) – A single record dictionary or a list of record dictionaries. +Each dictionary maps column schema names to values.

  • +
+
+
Returns:
+

A single GUID string for a single record, or a list of GUID +strings for bulk creation.

+
+
Return type:
+

str or list[str]

+
+
Raises:
+

TypeError – If data is not a dict or list[dict].

+
+
+

Example

+

Create a single record:

+
guid = client.records.create("account", {"name": "Contoso"})
+print(f"Created: {guid}")
+
+
+

Create multiple records:

+
guids = client.records.create("account", [
+    {"name": "Contoso"},
+    {"name": "Fabrikam"},
+])
+print(f"Created {len(guids)} accounts")
+
+
+
+ +
+
+update(table: str, ids: str | List[str], changes: Dict[str, Any] | List[Dict[str, Any]]) None
+

Update one or more records in a Dataverse table.

+

Supports three usage patterns:

+
    +
  1. Singleupdate("account", "guid", {"name": "New"})

  2. +
  3. Broadcastupdate("account", [id1, id2], {"status": 1}) +applies the same changes dict to every ID.

  4. +
  5. Pairedupdate("account", [id1, id2], [ch1, ch2]) +applies each changes dict to its corresponding ID (lists must be +equal length).

  6. +
+
+
Parameters:
+
    +
  • table (str) – Schema name of the table (e.g. "account").

  • +
  • ids (str or list[str]) – A single GUID string, or a list of GUID strings.

  • +
  • changes (dict or list[dict]) – A dictionary of field changes (single/broadcast), or a +list of dictionaries (paired, one per ID).

  • +
+
+
Raises:
+

TypeError – If ids is not str or list[str], or if changes +does not match the expected pattern.

+
+
+

Example

+

Single update:

+
client.records.update("account", account_id, {"telephone1": "555-0100"})
+
+
+

Broadcast update:

+
client.records.update("account", [id1, id2], {"statecode": 1})
+
+
+

Paired update:

+
client.records.update(
+    "account",
+    [id1, id2],
+    [{"name": "Name A"}, {"name": "Name B"}],
+)
+
+
+
+ +
+
+delete(table: str, ids: str) None
+
+delete(table: str, ids: List[str], *, use_bulk_delete: bool = True) str | None
+

Delete one or more records from a Dataverse table.

+

When ids is a single string, deletes that one record. When ids +is a list, either executes a BulkDelete action (returning the async job +ID) or deletes each record sequentially depending on use_bulk_delete.

+
+
Parameters:
+
    +
  • table (str) – Schema name of the table (e.g. "account").

  • +
  • ids (str or list[str]) – A single GUID string, or a list of GUID strings.

  • +
  • use_bulk_delete (bool) – When True (default) and ids is a list, use +the BulkDelete action and return its async job ID. When False, delete +records one at a time.

  • +
+
+
Returns:
+

The BulkDelete job ID when bulk-deleting; otherwise None.

+
+
Return type:
+

str or None

+
+
Raises:
+

TypeError – If ids is not str or list[str].

+
+
+

Example

+

Delete a single record:

+
client.records.delete("account", account_id)
+
+
+

Bulk delete:

+
job_id = client.records.delete("account", [id1, id2, id3])
+
+
+
+ +
+
+get(table: str, record_id: str, *, select: List[str] | None = None) PowerPlatform.Dataverse.models.record.Record
+
+get(table: str, *, select: List[str] | None = None, filter: str | None = None, orderby: List[str] | None = None, top: int | None = None, expand: List[str] | None = None, page_size: int | None = None, count: bool = False, include_annotations: str | None = None) Iterable[List[PowerPlatform.Dataverse.models.record.Record]]
+

Fetch a single record by ID, or fetch multiple records with pagination.

+

This method has two usage patterns:

+

Fetch a single recordget(table, record_id, *, select=...)

+

Pass record_id as a positional argument to retrieve one record +and get back a dict. Query parameters (filter, +orderby, top, expand, page_size) must not be provided.

+

Fetch multiple recordsget(table, *, select=..., filter=..., ...)

+

Omit record_id to perform a paginated fetch and get back a +generator that yields one page (list of record dicts) at a time. +Automatically follows @odata.nextLink for server-side paging.

+
+
Parameters:
+
    +
  • table (str) – Schema name of the table (e.g. "account" or +"new_MyTestTable").

  • +
  • record_id (str or None) – GUID of the record to retrieve. When omitted, +performs a multi-record fetch instead.

  • +
  • select (list[str] or None) – Optional list of column logical names to include. +Column names are automatically lowercased.

  • +
  • filter (str or None) – Optional OData $filter expression (e.g. +"name eq 'Contoso'"). Column names in filter expressions must +use exact lowercase logical names. Only used for multi-record +queries.

  • +
  • orderby (list[str] or None) – Optional list of sort expressions (e.g. +["name asc", "createdon desc"]). Column names are +automatically lowercased. Only used for multi-record queries.

  • +
  • top (int or None) – Optional maximum total number of records to return. Only +used for multi-record queries.

  • +
  • expand (list[str] or None) – Optional list of navigation properties to expand (e.g. +["primarycontactid"]). Case-sensitive; must match +server-defined names exactly. Only used for multi-record queries.

  • +
  • page_size (int or None) – Optional per-page size hint sent via +Prefer: odata.maxpagesize. Only used for multi-record queries.

  • +
  • count (bool) – If True, adds $count=true to include a total +record count in the response. Only used for multi-record queries.

  • +
  • include_annotations (str or None) – OData annotation pattern for the +Prefer: odata.include-annotations header (e.g. "*" or +"OData.Community.Display.V1.FormattedValue"), or None. +Only used for multi-record queries.

  • +
+
+
Returns:
+

A single Record +when record_id is provided, or a generator yielding pages +(lists of Record) +when fetching multiple records.

+
+
Return type:
+

Record or +collections.abc.Iterable[list[Record]]

+
+
Raises:
+
    +
  • TypeError – If record_id is provided but not a string.

  • +
  • ValueError – If query parameters are provided alongside +record_id.

  • +
+
+
+

Example

+

Fetch a single record:

+
record = client.records.get(
+    "account", account_id, select=["name", "telephone1"]
+)
+print(record["name"])
+
+
+

Fetch multiple records with pagination:

+
for page in client.records.get(
+    "account",
+    filter="statecode eq 0",
+    select=["name", "telephone1"],
+    page_size=50,
+):
+    for record in page:
+        print(record["name"])
+
+
+
+ +
+
+retrieve(table: str, record_id: str, *, select: List[str] | None = None, expand: List[str] | None = None, include_annotations: str | None = None) PowerPlatform.Dataverse.models.record.Record | None
+

Fetch a single record by its GUID, returning None if not found.

+

GA replacement for records.get(table, record_id). Returns None +instead of raising when the record does not exist (HTTP 404).

+
+
Parameters:
+
    +
  • table (str) – Schema name of the table (e.g. "account").

  • +
  • record_id (str) – GUID of the record to retrieve.

  • +
  • select (list[str] or None) – Optional list of column logical names to include.

  • +
  • expand (list[str] or None) – Optional list of navigation properties to expand (e.g. +["primarycontactid"]). Navigation property names are +case-sensitive and must match the entity’s $metadata.

  • +
  • include_annotations (str or None) – OData annotation pattern for the +Prefer: odata.include-annotations header (e.g. "*" or +"OData.Community.Display.V1.FormattedValue"), or None.

  • +
+
+
Returns:
+

Typed record, or None if not found.

+
+
Return type:
+

Record or None

+
+
+

Example:

+
record = client.records.retrieve(
+    "account", account_id,
+    select=["name", "statuscode"],
+    expand=["primarycontactid"],
+    include_annotations="OData.Community.Display.V1.FormattedValue",
+)
+if record is not None:
+    contact = record.get("primarycontactid") or {}
+    print(contact.get("fullname"))
+
+
+
+ +
+
+list(table: str, *, filter: str | PowerPlatform.Dataverse.models.filters.FilterExpression | None = None, select: List[str] | None = None, orderby: List[str] | None = None, top: int | None = None, expand: List[str] | None = None, page_size: int | None = None, count: bool = False, include_annotations: str | None = None) PowerPlatform.Dataverse.models.record.QueryResult
+

Fetch multiple records and return them as a QueryResult.

+

GA replacement for records.get(table, filter=...). All pages are +collected eagerly and returned as a single QueryResult.

+
+
Parameters:
+
    +
  • table (str) – Schema name of the table (e.g. "account").

  • +
  • filter (str or FilterExpression or None) – Optional OData filter string or FilterExpression.

  • +
  • select (list[str] or None) – Optional list of column logical names to include.

  • +
  • orderby (list[str] or None) – Optional list of sort expressions (e.g. ["name asc", "createdon desc"]).

  • +
  • top (int or None) – Maximum total number of records to return.

  • +
  • expand (list[str] or None) – Optional list of navigation properties to expand.

  • +
  • page_size (int or None) – Per-page size hint via Prefer: odata.maxpagesize.

  • +
  • count (bool) – If True, adds $count=true to include a total record count.

  • +
  • include_annotations (str or None) – OData annotation pattern for the +Prefer: odata.include-annotations header, or None.

  • +
+
+
Returns:
+

All matching records collected into a QueryResult.

+
+
Return type:
+

QueryResult

+
+
+

Example:

+
from PowerPlatform.Dataverse import col
+
+result = client.records.list(
+    "account",
+    filter=col("statecode") == 0,
+    select=["name", "statuscode"],
+    orderby=["name asc"],
+    top=100,
+    include_annotations="OData.Community.Display.V1.FormattedValue",
+)
+for record in result:
+    print(record["name"], record.get("statuscode@OData.Community.Display.V1.FormattedValue"))
+
+
+
+ +
+
+list_pages(table: str, *, filter: str | PowerPlatform.Dataverse.models.filters.FilterExpression | None = None, select: List[str] | None = None, orderby: List[str] | None = None, top: int | None = None, expand: List[str] | None = None, page_size: int | None = None, count: bool = False, include_annotations: str | None = None) Iterator[PowerPlatform.Dataverse.models.record.QueryResult]
+

Lazily yield one QueryResult per HTTP page.

+

Streaming counterpart to list(). Each iteration triggers one +network request via @odata.nextLink. One-shot — do not iterate +more than once.

+
+
Parameters:
+
    +
  • table (str) – Schema name of the table (e.g. "account").

  • +
  • filter (str or FilterExpression or None) – Optional OData filter string or FilterExpression.

  • +
  • select (list[str] or None) – Optional list of column logical names to include.

  • +
  • orderby (list[str] or None) – Optional list of sort expressions (e.g. ["name asc", "createdon desc"]).

  • +
  • top (int or None) – Maximum total number of records to return.

  • +
  • expand (list[str] or None) – Optional list of navigation properties to expand.

  • +
  • page_size (int or None) – Per-page size hint via Prefer: odata.maxpagesize.

  • +
  • count (bool) – If True, adds $count=true to include a total record count.

  • +
  • include_annotations (str or None) – OData annotation pattern for the +Prefer: odata.include-annotations header, or None.

  • +
+
+
Returns:
+

Iterator of per-page QueryResult objects.

+
+
Return type:
+

Iterator[QueryResult]

+
+
+

Example:

+
for page in client.records.list_pages(
+    "account",
+    filter="statecode eq 0",
+    orderby=["name asc"],
+    page_size=200,
+):
+    process(page.to_dataframe())
+
+
+
+ +
+
+upsert(table: str, items: List[PowerPlatform.Dataverse.models.upsert.UpsertItem | Dict[str, Any]]) None
+

Upsert one or more records identified by alternate keys.

+

When items contains a single entry, performs a single upsert via PATCH +using the alternate key in the URL. When items contains multiple entries, +uses the UpsertMultiple bulk action.

+

Each item must be either a UpsertItem +or a plain dict with "alternate_key" and "record" keys (both dicts).

+
+
Parameters:
+
    +
  • table (str) – Schema name of the table (e.g. "account" or "new_MyTestTable").

  • +
  • items (list[UpsertItem | dict]) – Non-empty list of UpsertItem +instances or dicts with "alternate_key" and "record" keys.

  • +
+
+
Returns:
+

None

+
+
Return type:
+

None

+
+
Raises:
+

TypeError – If items is not a non-empty list, or if any element is +neither a UpsertItem nor a +dict with "alternate_key" and "record" keys.

+
+
+

Example

+

Upsert a single record using UpsertItem:

+
from PowerPlatform.Dataverse.models import UpsertItem
+
+client.records.upsert("account", [
+    UpsertItem(
+        alternate_key={"accountnumber": "ACC-001"},
+        record={"name": "Contoso Ltd", "description": "Primary account"},
+    )
+])
+
+
+

Upsert a single record using a plain dict:

+
client.records.upsert("account", [
+    {
+        "alternate_key": {"accountnumber": "ACC-001"},
+        "record": {"name": "Contoso Ltd", "description": "Primary account"},
+    },
+])
+
+
+

Upsert multiple records using UpsertItem:

+
from PowerPlatform.Dataverse.models import UpsertItem
+
+client.records.upsert("account", [
+    UpsertItem(
+        alternate_key={"accountnumber": "ACC-001"},
+        record={"name": "Contoso Ltd", "description": "Primary account"},
+    ),
+    UpsertItem(
+        alternate_key={"accountnumber": "ACC-002"},
+        record={"name": "Fabrikam Inc", "description": "Partner account"},
+    ),
+])
+
+
+

Upsert multiple records using plain dicts:

+
client.records.upsert("account", [
+    {
+        "alternate_key": {"accountnumber": "ACC-001"},
+        "record": {"name": "Contoso Ltd", "description": "Primary account"},
+    },
+    {
+        "alternate_key": {"accountnumber": "ACC-002"},
+        "record": {"name": "Fabrikam Inc", "description": "Partner account"},
+    },
+])
+
+
+

The alternate_key dict may contain multiple columns when the configured +alternate key is composite, e.g. +{"accountnumber": "ACC-001", "address1_postalcode": "98052"}.

+
+ +
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/tables/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/tables/index.html new file mode 100644 index 00000000..f1dc30b7 --- /dev/null +++ b/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/tables/index.html @@ -0,0 +1,865 @@ + + + + + + + + PowerPlatform.Dataverse.operations.tables — PowerPlatform-Dataverse-Client 0.1.0b11 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

PowerPlatform.Dataverse.operations.tables

+

Table metadata operations namespace for the Dataverse SDK.

+
+

Classes

+ + + + + + +

TableOperations

Namespace for table-level metadata operations.

+
+
+

Module Contents

+
+
+class PowerPlatform.Dataverse.operations.tables.TableOperations(client: PowerPlatform.Dataverse.client.DataverseClient)
+

Namespace for table-level metadata operations.

+

Accessed via client.tables. Provides operations to create, delete, +inspect, and list Dataverse tables, as well as add and remove columns.

+
+
Parameters:
+

client (DataverseClient) – The parent DataverseClient instance.

+
+
+

Example:

+
client = DataverseClient(base_url, credential)
+
+# Create a table
+info = client.tables.create(
+    "new_Product",
+    {"new_Price": "decimal", "new_InStock": "bool"},
+    solution="MySolution",
+)
+
+# List tables
+tables = client.tables.list()
+
+# Get table info
+info = client.tables.get("new_Product")
+
+# Add columns
+client.tables.add_columns("new_Product", {"new_Rating": "int"})
+
+# Remove columns
+client.tables.remove_columns("new_Product", "new_Rating")
+
+# Delete a table
+client.tables.delete("new_Product")
+
+
+
+
+create(table: str, columns: Dict[str, Any], *, solution: str | None = None, primary_column: str | None = None, display_name: str | None = None) PowerPlatform.Dataverse.models.table_info.TableInfo
+

Create a custom table with the specified columns.

+
+
Parameters:
+
    +
  • table (str) – Schema name of the table with customization prefix +(e.g. "new_MyTestTable").

  • +
  • columns (dict) – Mapping of column schema names (with customization +prefix) to their types. Supported types include "string" +(or "text"), "memo" (or "multiline"), +"int" (or "integer"), "decimal" +(or "money"), "float" (or "double"), "datetime" +(or "date"), "bool" (or "boolean"), "file", and +Enum subclasses +(for local option sets).

  • +
  • solution (str or None) – Optional solution unique name that should own the new +table. When omitted the table is created in the default solution.

  • +
  • primary_column (str or None) – Optional primary name column schema name with +customization prefix (e.g. "new_ProductName"). If not provided, +defaults to "{prefix}_Name".

  • +
  • display_name (str or None) – Human-readable display name for the table +(e.g. "Product"). When omitted, defaults to the table schema name.

  • +
+
+
Returns:
+

Table metadata with schema_name, entity_set_name, +logical_name, metadata_id, and columns_created. +Supports dict-like access with legacy keys for backward +compatibility.

+
+
Return type:
+

TableInfo

+
+
Raises:
+

MetadataError – If table creation fails or the table already exists.

+
+
+

Example

+

Create a table with simple columns:

+
from enum import IntEnum
+
+class ItemStatus(IntEnum):
+    ACTIVE = 1
+    INACTIVE = 2
+
+result = client.tables.create(
+    "new_Product",
+    {
+        "new_Title": "string",
+        "new_Price": "decimal",
+        "new_Status": ItemStatus,
+    },
+    solution="MySolution",
+    primary_column="new_ProductName",
+    display_name="Product",
+)
+print(f"Created: {result['table_schema_name']}")
+
+
+
+ +
+
+delete(table: str) None
+

Delete a custom table by schema name.

+
+
Parameters:
+

table (str) – Schema name of the table (e.g. "new_MyTestTable").

+
+
Raises:
+

MetadataError – If the table does not exist or deletion fails.

+
+
+
+

Warning

+

This operation is irreversible and will delete all records in the +table along with the table definition.

+
+

Example:

+
client.tables.delete("new_MyTestTable")
+
+
+
+ +
+
+get(table: str) PowerPlatform.Dataverse.models.table_info.TableInfo | None
+

Get basic metadata for a table if it exists.

+
+
Parameters:
+

table (str) – Schema name of the table (e.g. "new_MyTestTable" +or "account").

+
+
Returns:
+

Table metadata, or None if the table is not found. +Supports dict-like access with legacy keys for backward +compatibility.

+
+
Return type:
+

TableInfo +or None

+
+
+

Example:

+
info = client.tables.get("new_MyTestTable")
+if info:
+    print(f"Logical name: {info['table_logical_name']}")
+    print(f"Entity set: {info['entity_set_name']}")
+
+
+
+ +
+
+list(*, filter: str | None = None, select: List[str] | None = None) List[Dict[str, Any]]
+

List all non-private tables in the Dataverse environment.

+

By default returns every table where IsPrivate eq false. Supply +an optional OData $filter expression to further narrow the results. +The expression is combined with the default IsPrivate eq false +clause using and.

+
+
Parameters:
+
    +
  • filter (str or None) – Optional OData $filter expression to further narrow +the list of returned tables (e.g. +"SchemaName eq 'Account'"). Column names in filter +expressions must use the exact property names from the +EntityDefinitions metadata (typically PascalCase).

  • +
  • select (list[str] or None) – Optional list of property names to include in the +response (projected via the OData $select query option). +Property names must use the exact PascalCase names from the +EntityDefinitions metadata (e.g. +["LogicalName", "SchemaName", "DisplayName"]). +When None (the default) or an empty list, all properties are +returned.

  • +
+
+
Returns:
+

List of EntityDefinition metadata dictionaries.

+
+
Return type:
+

list[dict]

+
+
+

Example:

+
# List all non-private tables
+tables = client.tables.list()
+for table in tables:
+    print(table["LogicalName"])
+
+# List only tables whose schema name starts with "new_"
+custom_tables = client.tables.list(
+    filter="startswith(SchemaName, 'new_')"
+)
+
+# List tables with only specific properties
+tables = client.tables.list(
+    select=["LogicalName", "SchemaName", "EntitySetName"]
+)
+
+
+
+ +
+
+add_columns(table: str, columns: Dict[str, Any]) List[str]
+

Add one or more columns to an existing table.

+
+
Parameters:
+
    +
  • table (str) – Schema name of the table (e.g. "new_MyTestTable").

  • +
  • columns (dict) – Mapping of column schema names (with customization +prefix) to their types. Supported types are the same as for +create().

  • +
+
+
Returns:
+

Schema names of the columns that were created.

+
+
Return type:
+

list[str]

+
+
Raises:
+

MetadataError – If the table does not exist.

+
+
+

Example:

+
created = client.tables.add_columns(
+    "new_MyTestTable",
+    {"new_Notes": "string", "new_Active": "bool"},
+)
+print(created)  # ['new_Notes', 'new_Active']
+
+
+
+ +
+
+remove_columns(table: str, columns: str | List[str]) List[str]
+

Remove one or more columns from a table.

+
+
Parameters:
+
    +
  • table (str) – Schema name of the table (e.g. "new_MyTestTable").

  • +
  • columns (str or list[str]) – Column schema name or list of column schema names to +remove. Must include the customization prefix (e.g. +"new_TestColumn").

  • +
+
+
Returns:
+

Schema names of the columns that were removed.

+
+
Return type:
+

list[str]

+
+
Raises:
+

MetadataError – If the table or a specified column does not exist.

+
+
+

Example:

+
removed = client.tables.remove_columns(
+    "new_MyTestTable",
+    ["new_Notes", "new_Active"],
+)
+print(removed)  # ['new_Notes', 'new_Active']
+
+
+
+ +
+
+create_one_to_many_relationship(lookup: PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata, relationship: PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata, *, solution: str | None = None) PowerPlatform.Dataverse.models.relationship.RelationshipInfo
+

Create a one-to-many relationship between tables.

+

This operation creates both the relationship and the lookup attribute +on the referencing table.

+
+
Parameters:
+
+
+
Returns:
+

Relationship metadata with relationship_id, +relationship_schema_name, relationship_type, +lookup_schema_name, referenced_entity, and +referencing_entity.

+
+
Return type:
+

RelationshipInfo

+
+
Raises:
+

HttpError – If the Web API request fails.

+
+
+

Example

+

Create a one-to-many relationship: Department (1) -> Employee (N):

+
from PowerPlatform.Dataverse.models import (
+    LookupAttributeMetadata,
+    OneToManyRelationshipMetadata,
+    Label,
+    LocalizedLabel,
+    CascadeConfiguration,
+)
+from PowerPlatform.Dataverse.common.constants import (
+    CASCADE_BEHAVIOR_REMOVE_LINK,
+)
+
+lookup = LookupAttributeMetadata(
+    schema_name="new_DepartmentId",
+    display_name=Label(
+        localized_labels=[
+            LocalizedLabel(label="Department", language_code=1033)
+        ]
+    ),
+)
+
+relationship = OneToManyRelationshipMetadata(
+    schema_name="new_Department_Employee",
+    referenced_entity="new_department",
+    referencing_entity="new_employee",
+    referenced_attribute="new_departmentid",
+    cascade_configuration=CascadeConfiguration(
+        delete=CASCADE_BEHAVIOR_REMOVE_LINK,
+    ),
+)
+
+result = client.tables.create_one_to_many_relationship(lookup, relationship)
+print(f"Created lookup field: {result.lookup_schema_name}")
+
+
+
+ +
+
+create_many_to_many_relationship(relationship: PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata, *, solution: str | None = None) PowerPlatform.Dataverse.models.relationship.RelationshipInfo
+

Create a many-to-many relationship between tables.

+

This operation creates a many-to-many relationship and an intersect +table to manage the relationship.

+
+
Parameters:
+
    +
  • relationship (ManyToManyRelationshipMetadata) – Metadata defining the many-to-many relationship.

  • +
  • solution (str or None) – Optional solution unique name to add relationship to.

  • +
+
+
Returns:
+

Relationship metadata with relationship_id, +relationship_schema_name, relationship_type, +entity1_logical_name, and entity2_logical_name.

+
+
Return type:
+

RelationshipInfo

+
+
Raises:
+

HttpError – If the Web API request fails.

+
+
+

Example

+

Create a many-to-many relationship: Employee <-> Project:

+
from PowerPlatform.Dataverse.models import (
+    ManyToManyRelationshipMetadata,
+)
+
+relationship = ManyToManyRelationshipMetadata(
+    schema_name="new_employee_project",
+    entity1_logical_name="new_employee",
+    entity2_logical_name="new_project",
+)
+
+result = client.tables.create_many_to_many_relationship(relationship)
+print(f"Created: {result.relationship_schema_name}")
+
+
+
+ +
+
+delete_relationship(relationship_id: str) None
+

Delete a relationship by its metadata ID.

+
+
Parameters:
+

relationship_id (str) – The GUID of the relationship metadata.

+
+
Raises:
+

HttpError – If the Web API request fails.

+
+
+
+

Warning

+

Deleting a relationship also removes the associated lookup attribute +for one-to-many relationships. This operation is irreversible.

+
+

Example:

+
client.tables.delete_relationship(
+    "12345678-1234-1234-1234-123456789abc"
+)
+
+
+
+ +
+
+get_relationship(schema_name: str) PowerPlatform.Dataverse.models.relationship.RelationshipInfo | None
+

Retrieve relationship metadata by schema name.

+
+
Parameters:
+

schema_name (str) – The schema name of the relationship.

+
+
Returns:
+

Relationship metadata, or None if not found.

+
+
Return type:
+

RelationshipInfo +or None

+
+
Raises:
+

HttpError – If the Web API request fails.

+
+
+

Example:

+
rel = client.tables.get_relationship("new_Department_Employee")
+if rel:
+    print(f"Found: {rel.relationship_schema_name}")
+
+
+
+ +
+
+create_lookup_field(referencing_table: str, lookup_field_name: str, referenced_table: str, *, display_name: str | None = None, description: str | None = None, required: bool = False, cascade_delete: str = CASCADE_BEHAVIOR_REMOVE_LINK, solution: str | None = None, language_code: int = 1033) PowerPlatform.Dataverse.models.relationship.RelationshipInfo
+

Create a simple lookup field relationship.

+

This is a convenience method that wraps create_one_to_many_relationship() +for the common case of adding a lookup field to an existing table.

+
+
Parameters:
+
    +
  • referencing_table (str) – Logical name of the table that will have +the lookup field (child table).

  • +
  • lookup_field_name (str) – Schema name for the lookup field +(e.g., "new_AccountId").

  • +
  • referenced_table (str) – Logical name of the table being referenced +(parent table).

  • +
  • display_name (str or None) – Display name for the lookup field. Defaults to +the referenced table name.

  • +
  • description (str or None) – Optional description for the lookup field.

  • +
  • required (bool) – Whether the lookup is required. Defaults to False.

  • +
  • cascade_delete (str) – Delete behavior ("RemoveLink", +"Cascade", "Restrict"). Defaults to "RemoveLink".

  • +
  • solution (str or None) – Optional solution unique name to add the relationship +to.

  • +
  • language_code (int) – Language code for labels. Defaults to 1033 +(English).

  • +
+
+
Returns:
+

Relationship metadata with relationship_id, +relationship_schema_name, relationship_type, +lookup_schema_name, referenced_entity, and +referencing_entity.

+
+
Return type:
+

RelationshipInfo

+
+
Raises:
+

HttpError – If the Web API request fails.

+
+
+

Example

+

Create a simple lookup field:

+
result = client.tables.create_lookup_field(
+    referencing_table="new_order",
+    lookup_field_name="new_AccountId",
+    referenced_table="account",
+    display_name="Account",
+    required=True,
+    cascade_delete=CASCADE_BEHAVIOR_REMOVE_LINK,
+)
+print(f"Created lookup: {result['lookup_schema_name']}")
+
+
+
+ +
+
+create_alternate_key(table: str, key_name: str, columns: List[str], *, display_name: str | None = None, language_code: int = 1033) PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo
+

Create an alternate key on a table.

+

Alternate keys allow upsert operations to identify records by one or +more columns instead of the primary GUID. After creation the key is +queued for index building; its status will +transition from "Pending" to "Active" once the index is ready.

+
+
Parameters:
+
    +
  • table (str) – Schema name of the table (e.g. "new_Product").

  • +
  • key_name (str) – Schema name for the new alternate key +(e.g. "new_product_code_key").

  • +
  • columns (list[str]) – List of column logical names that compose the key +(e.g. ["new_productcode"]).

  • +
  • display_name (str or None) – Display name for the key. Defaults to +key_name if not provided.

  • +
  • language_code (int) – Language code for labels. Defaults to 1033 +(English).

  • +
+
+
Returns:
+

Metadata for the newly created alternate key.

+
+
Return type:
+

AlternateKeyInfo

+
+
Raises:
+
+
+
+

Example

+

Create a single-column alternate key for upsert:

+
key = client.tables.create_alternate_key(
+    "new_Product",
+    "new_product_code_key",
+    ["new_productcode"],
+    display_name="Product Code",
+)
+print(f"Key ID: {key.metadata_id}")
+print(f"Columns: {key.key_attributes}")
+
+
+
+ +
+
+get_alternate_keys(table: str) List[PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo]
+

List all alternate keys defined on a table.

+
+
Parameters:
+

table (str) – Schema name of the table (e.g. "new_Product").

+
+
Returns:
+

List of alternate key metadata objects. May be empty if no +alternate keys are defined.

+
+
Return type:
+

list[AlternateKeyInfo]

+
+
Raises:
+
+
+
+

Example

+

List alternate keys and print their status:

+
keys = client.tables.get_alternate_keys("new_Product")
+for key in keys:
+    print(f"{key.schema_name}: {key.status}")
+
+
+
+ +
+
+delete_alternate_key(table: str, key_id: str) None
+

Delete an alternate key by its metadata ID.

+
+
Parameters:
+
    +
  • table (str) – Schema name of the table (e.g. "new_Product").

  • +
  • key_id (str) – Metadata GUID of the alternate key to delete.

  • +
+
+
Raises:
+
+
+
+
+

Warning

+

Deleting an alternate key that is in use by upsert operations will +cause those operations to fail. This operation is irreversible.

+
+

Example:

+
client.tables.delete_alternate_key(
+    "new_Product",
+    "12345678-1234-1234-1234-123456789abc",
+)
+
+
+
+ +
+
+list_columns(table: str, *, select: List[str] | None = None, filter: str | None = None) List[Dict[str, Any]]
+

List all attribute (column) definitions for a table.

+
+
Parameters:
+
    +
  • table (str) – Schema name of the table (e.g. "account" or +"new_Product").

  • +
  • select (list[str] or None) – Optional list of property names to project via +$select. Values are passed as-is (PascalCase).

  • +
  • filter (str or None) – Optional OData $filter expression. For example, +"AttributeType eq 'String'" returns only string columns.

  • +
+
+
Returns:
+

List of raw attribute metadata dictionaries.

+
+
Return type:
+

list[dict[str, Any]]

+
+
Raises:
+
+
+
+

Example:

+
# List all columns on the account table
+columns = client.tables.list_columns("account")
+for col in columns:
+    print(f"{col['LogicalName']} ({col.get('AttributeType')})")
+
+# List only specific properties
+columns = client.tables.list_columns(
+    "account",
+    select=["LogicalName", "SchemaName", "AttributeType"],
+)
+
+# Filter to only string attributes
+columns = client.tables.list_columns(
+    "account",
+    filter="AttributeType eq 'String'",
+)
+
+
+
+ +
+
+list_relationships(*, filter: str | None = None, select: List[str] | None = None) List[Dict[str, Any]]
+

List all relationship definitions in the environment.

+
+
Parameters:
+
    +
  • filter (str or None) – Optional OData $filter expression. For example, +"RelationshipType eq Microsoft.Dynamics.CRM.RelationshipType'OneToManyRelationship'" +returns only one-to-many relationships.

  • +
  • select (list[str] or None) – Optional list of property names to project via +$select. Values are passed as-is (PascalCase).

  • +
+
+
Returns:
+

List of raw relationship metadata dictionaries.

+
+
Return type:
+

list[dict[str, Any]]

+
+
Raises:
+

HttpError – If the Web API request fails.

+
+
+

Example:

+
# List all relationships
+rels = client.tables.list_relationships()
+for rel in rels:
+    print(f"{rel['SchemaName']} ({rel.get('@odata.type')})")
+
+# Filter by type
+one_to_many = client.tables.list_relationships(
+    filter="RelationshipType eq Microsoft.Dynamics.CRM.RelationshipType'OneToManyRelationship'"
+)
+
+# Select specific properties
+rels = client.tables.list_relationships(
+    select=["SchemaName", "ReferencedEntity", "ReferencingEntity"]
+)
+
+
+
+ +
+
+list_table_relationships(table: str, *, filter: str | None = None, select: List[str] | None = None) List[Dict[str, Any]]
+

List all relationships for a specific table.

+

Combines one-to-many, many-to-one, and many-to-many relationships +for the given table by querying +EntityDefinitions({id})/OneToManyRelationships, +EntityDefinitions({id})/ManyToOneRelationships, and +EntityDefinitions({id})/ManyToManyRelationships.

+
+
Parameters:
+
    +
  • table (str) – Schema name of the table (e.g. "account").

  • +
  • filter (str or None) – Optional OData $filter expression applied to each +sub-request.

  • +
  • select (list[str] or None) – Optional list of property names to project via +$select. Values are passed as-is (PascalCase).

  • +
+
+
Returns:
+

Combined list of one-to-many, many-to-one, and many-to-many +relationship metadata dictionaries.

+
+
Return type:
+

list[dict[str, Any]]

+
+
Raises:
+
+
+
+

Example:

+
# List all relationships for the account table
+rels = client.tables.list_table_relationships("account")
+for rel in rels:
+    print(f"{rel['SchemaName']} -> {rel.get('@odata.type')}")
+
+
+
+ +
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/utils/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/utils/index.html new file mode 100644 index 00000000..8783fde1 --- /dev/null +++ b/docs_local/_build/autoapi/PowerPlatform/Dataverse/utils/index.html @@ -0,0 +1,118 @@ + + + + + + + + PowerPlatform.Dataverse.utils — PowerPlatform-Dataverse-Client 0.1.0b11 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

PowerPlatform.Dataverse.utils

+

Utilities and adapters for the Dataverse SDK.

+

Placeholder module for future utility adapters.

+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/index.html b/docs_local/_build/autoapi/PowerPlatform/index.html new file mode 100644 index 00000000..06f01e1f --- /dev/null +++ b/docs_local/_build/autoapi/PowerPlatform/index.html @@ -0,0 +1,122 @@ + + + + + + + + PowerPlatform — PowerPlatform-Dataverse-Client 0.1.0b11 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

PowerPlatform

+
+

Submodules

+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs_local/_build/autoapi/index.html b/docs_local/_build/autoapi/index.html new file mode 100644 index 00000000..5707b4a9 --- /dev/null +++ b/docs_local/_build/autoapi/index.html @@ -0,0 +1,170 @@ + + + + + + + + API Reference — PowerPlatform-Dataverse-Client 0.1.0b11 documentation + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ + + + + + + \ No newline at end of file diff --git a/docs_local/_build/genindex.html b/docs_local/_build/genindex.html new file mode 100644 index 00000000..24bcfa08 --- /dev/null +++ b/docs_local/_build/genindex.html @@ -0,0 +1,1331 @@ + + + + + + + Index — PowerPlatform-Dataverse-Client 0.1.0b11 documentation + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ + +

Index

+ +
+ A + | B + | C + | D + | E + | F + | G + | H + | I + | K + | L + | M + | N + | O + | P + | Q + | R + | S + | T + | U + | V + +
+

A

+ + + +
+ +

B

+ + + +
+ +

C

+ + + +
+ +

D

+ + + +
+ +

E

+ + + +
+ +

F

+ + + +
+ +

G

+ + + +
+ +

H

+ + + +
+ +

I

+ + + +
+ +

K

+ + + +
+ +

L

+ + + +
+ +

M

+ + +
+ +

N

+ + + +
+ +

O

+ + + +
+ +

P

+ + + +
    +
  • page_size (PowerPlatform.Dataverse.models.query_builder.QueryParams attribute) +
  • +
  • + PowerPlatform + +
  • +
  • + PowerPlatform.Dataverse + +
  • +
  • + PowerPlatform.Dataverse.claude_skill + +
  • +
  • + PowerPlatform.Dataverse.client + +
  • +
  • + PowerPlatform.Dataverse.common + +
  • +
  • + PowerPlatform.Dataverse.common.constants + +
  • +
  • + PowerPlatform.Dataverse.core + +
  • +
  • + PowerPlatform.Dataverse.core.config + +
  • +
  • + PowerPlatform.Dataverse.core.errors + +
  • +
  • + PowerPlatform.Dataverse.core.log_config + +
  • +
  • + PowerPlatform.Dataverse.data + +
  • +
  • + PowerPlatform.Dataverse.extensions + +
  • +
  • + PowerPlatform.Dataverse.migration + +
  • +
  • + PowerPlatform.Dataverse.migration.migrate_v0_to_v1 + +
  • +
  • + PowerPlatform.Dataverse.models + +
  • +
  • + PowerPlatform.Dataverse.models.batch + +
  • +
  • + PowerPlatform.Dataverse.models.fetchxml_query + +
  • +
+ +

Q

+ + + +
+ +

R

+ + + +
+ +

S

+ + + +
+ +

T

+ + + +
+ +

U

+ + + +
+ +

V

+ + + +
+ + + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs_local/_build/index.html b/docs_local/_build/index.html new file mode 100644 index 00000000..f28a35e0 --- /dev/null +++ b/docs_local/_build/index.html @@ -0,0 +1,116 @@ + + + + + + + + PowerPlatform Dataverse Client SDK — PowerPlatform-Dataverse-Client 0.1.0b11 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

PowerPlatform Dataverse Client SDK

+
+

Contents:

+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs_local/_build/objects.inv b/docs_local/_build/objects.inv new file mode 100644 index 0000000000000000000000000000000000000000..56d18ab10ee804d18f7f6cab46a4c0047dad9f16 GIT binary patch literal 3506 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 g-LK@5mAle{V4f~_1 + + + + + + Python Module Index — PowerPlatform-Dataverse-Client 0.1.0b11 documentation + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ + +

Python Module Index

+ +
+ p +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
+ p
+ PowerPlatform +
    + PowerPlatform.Dataverse +
    + PowerPlatform.Dataverse.claude_skill +
    + PowerPlatform.Dataverse.client +
    + PowerPlatform.Dataverse.common +
    + PowerPlatform.Dataverse.common.constants +
    + PowerPlatform.Dataverse.core +
    + PowerPlatform.Dataverse.core.config +
    + PowerPlatform.Dataverse.core.errors +
    + PowerPlatform.Dataverse.core.log_config +
    + PowerPlatform.Dataverse.data +
    + PowerPlatform.Dataverse.extensions +
    + PowerPlatform.Dataverse.migration +
    + PowerPlatform.Dataverse.migration.migrate_v0_to_v1 +
    + PowerPlatform.Dataverse.models +
    + PowerPlatform.Dataverse.models.batch +
    + PowerPlatform.Dataverse.models.fetchxml_query +
    + PowerPlatform.Dataverse.models.filters +
    + PowerPlatform.Dataverse.models.labels +
    + PowerPlatform.Dataverse.models.protocol +
    + PowerPlatform.Dataverse.models.query_builder +
    + PowerPlatform.Dataverse.models.record +
    + PowerPlatform.Dataverse.models.relationship +
    + PowerPlatform.Dataverse.models.table_info +
    + PowerPlatform.Dataverse.models.upsert +
    + PowerPlatform.Dataverse.operations +
    + PowerPlatform.Dataverse.operations.batch +
    + PowerPlatform.Dataverse.operations.dataframe +
    + PowerPlatform.Dataverse.operations.files +
    + PowerPlatform.Dataverse.operations.query +
    + PowerPlatform.Dataverse.operations.records +
    + PowerPlatform.Dataverse.operations.tables +
    + PowerPlatform.Dataverse.utils +
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs_local/_build/search.html b/docs_local/_build/search.html new file mode 100644 index 00000000..36557a12 --- /dev/null +++ b/docs_local/_build/search.html @@ -0,0 +1,121 @@ + + + + + + + Search — PowerPlatform-Dataverse-Client 0.1.0b11 documentation + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +

Search

+ + + + +

+ Searching for multiple words only shows matches that contain + all words. +

+ + +
+ + + +
+ + +
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs_local/_build/searchindex.js b/docs_local/_build/searchindex.js new file mode 100644 index 00000000..21679509 --- /dev/null +++ b/docs_local/_build/searchindex.js @@ -0,0 +1 @@ +Search.setIndex({"alltitles":{"API Reference":[[33,null]],"Attributes":[[2,"attributes"]],"Classes":[[1,"classes"],[4,"classes"],[7,"classes"],[13,"classes"],[14,"classes"],[15,"classes"],[17,"classes"],[18,"classes"],[19,"classes"],[20,"classes"],[21,"classes"],[22,"classes"],[23,"classes"],[24,"classes"],[25,"classes"],[26,"classes"],[28,"classes"],[29,"classes"],[30,"classes"]],"Contents:":[[34,null]],"Exceptions":[[5,"exceptions"]],"Functions":[[12,"functions"],[15,"functions"]],"Module Contents":[[1,"module-contents"],[2,"module-contents"],[4,"module-contents"],[5,"module-contents"],[7,"module-contents"],[12,"module-contents"],[13,"module-contents"],[14,"module-contents"],[15,"module-contents"],[17,"module-contents"],[18,"module-contents"],[19,"module-contents"],[20,"module-contents"],[21,"module-contents"],[22,"module-contents"],[23,"module-contents"],[24,"module-contents"],[25,"module-contents"],[26,"module-contents"],[28,"module-contents"],[29,"module-contents"],[30,"module-contents"]],"PowerPlatform":[[32,null]],"PowerPlatform Dataverse Client SDK":[[34,null]],"PowerPlatform.Dataverse":[[10,null]],"PowerPlatform.Dataverse.claude_skill":[[0,null]],"PowerPlatform.Dataverse.client":[[1,null]],"PowerPlatform.Dataverse.common":[[3,null]],"PowerPlatform.Dataverse.common.constants":[[2,null]],"PowerPlatform.Dataverse.core":[[6,null]],"PowerPlatform.Dataverse.core.config":[[4,null]],"PowerPlatform.Dataverse.core.errors":[[5,null]],"PowerPlatform.Dataverse.core.log_config":[[7,null]],"PowerPlatform.Dataverse.data":[[8,null]],"PowerPlatform.Dataverse.extensions":[[9,null]],"PowerPlatform.Dataverse.migration":[[11,null]],"PowerPlatform.Dataverse.migration.migrate_v0_to_v1":[[12,null]],"PowerPlatform.Dataverse.models":[[16,null]],"PowerPlatform.Dataverse.models.batch":[[13,null]],"PowerPlatform.Dataverse.models.fetchxml_query":[[14,null]],"PowerPlatform.Dataverse.models.filters":[[15,null]],"PowerPlatform.Dataverse.models.labels":[[17,null]],"PowerPlatform.Dataverse.models.protocol":[[18,null]],"PowerPlatform.Dataverse.models.query_builder":[[19,null]],"PowerPlatform.Dataverse.models.record":[[20,null]],"PowerPlatform.Dataverse.models.relationship":[[21,null]],"PowerPlatform.Dataverse.models.table_info":[[22,null]],"PowerPlatform.Dataverse.models.upsert":[[23,null]],"PowerPlatform.Dataverse.operations":[[27,null]],"PowerPlatform.Dataverse.operations.batch":[[24,null]],"PowerPlatform.Dataverse.operations.dataframe":[[25,null]],"PowerPlatform.Dataverse.operations.files":[[26,null]],"PowerPlatform.Dataverse.operations.query":[[28,null]],"PowerPlatform.Dataverse.operations.records":[[29,null]],"PowerPlatform.Dataverse.operations.tables":[[30,null]],"PowerPlatform.Dataverse.utils":[[31,null]],"Submodules":[[3,"submodules"],[6,"submodules"],[10,"submodules"],[11,"submodules"],[16,"submodules"],[27,"submodules"],[32,"submodules"]],"Transformations applied":[[12,"transformations-applied"]]},"docnames":["autoapi/PowerPlatform/Dataverse/claude_skill/index","autoapi/PowerPlatform/Dataverse/client/index","autoapi/PowerPlatform/Dataverse/common/constants/index","autoapi/PowerPlatform/Dataverse/common/index","autoapi/PowerPlatform/Dataverse/core/config/index","autoapi/PowerPlatform/Dataverse/core/errors/index","autoapi/PowerPlatform/Dataverse/core/index","autoapi/PowerPlatform/Dataverse/core/log_config/index","autoapi/PowerPlatform/Dataverse/data/index","autoapi/PowerPlatform/Dataverse/extensions/index","autoapi/PowerPlatform/Dataverse/index","autoapi/PowerPlatform/Dataverse/migration/index","autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index","autoapi/PowerPlatform/Dataverse/models/batch/index","autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index","autoapi/PowerPlatform/Dataverse/models/filters/index","autoapi/PowerPlatform/Dataverse/models/index","autoapi/PowerPlatform/Dataverse/models/labels/index","autoapi/PowerPlatform/Dataverse/models/protocol/index","autoapi/PowerPlatform/Dataverse/models/query_builder/index","autoapi/PowerPlatform/Dataverse/models/record/index","autoapi/PowerPlatform/Dataverse/models/relationship/index","autoapi/PowerPlatform/Dataverse/models/table_info/index","autoapi/PowerPlatform/Dataverse/models/upsert/index","autoapi/PowerPlatform/Dataverse/operations/batch/index","autoapi/PowerPlatform/Dataverse/operations/dataframe/index","autoapi/PowerPlatform/Dataverse/operations/files/index","autoapi/PowerPlatform/Dataverse/operations/index","autoapi/PowerPlatform/Dataverse/operations/query/index","autoapi/PowerPlatform/Dataverse/operations/records/index","autoapi/PowerPlatform/Dataverse/operations/tables/index","autoapi/PowerPlatform/Dataverse/utils/index","autoapi/PowerPlatform/index","autoapi/index","index"],"envversion":{"sphinx":66,"sphinx.domains.c":3,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":9,"sphinx.domains.index":1,"sphinx.domains.javascript":3,"sphinx.domains.math":2,"sphinx.domains.python":4,"sphinx.domains.rst":2,"sphinx.domains.std":2},"filenames":["autoapi\\PowerPlatform\\Dataverse\\claude_skill\\index.rst","autoapi\\PowerPlatform\\Dataverse\\client\\index.rst","autoapi\\PowerPlatform\\Dataverse\\common\\constants\\index.rst","autoapi\\PowerPlatform\\Dataverse\\common\\index.rst","autoapi\\PowerPlatform\\Dataverse\\core\\config\\index.rst","autoapi\\PowerPlatform\\Dataverse\\core\\errors\\index.rst","autoapi\\PowerPlatform\\Dataverse\\core\\index.rst","autoapi\\PowerPlatform\\Dataverse\\core\\log_config\\index.rst","autoapi\\PowerPlatform\\Dataverse\\data\\index.rst","autoapi\\PowerPlatform\\Dataverse\\extensions\\index.rst","autoapi\\PowerPlatform\\Dataverse\\index.rst","autoapi\\PowerPlatform\\Dataverse\\migration\\index.rst","autoapi\\PowerPlatform\\Dataverse\\migration\\migrate_v0_to_v1\\index.rst","autoapi\\PowerPlatform\\Dataverse\\models\\batch\\index.rst","autoapi\\PowerPlatform\\Dataverse\\models\\fetchxml_query\\index.rst","autoapi\\PowerPlatform\\Dataverse\\models\\filters\\index.rst","autoapi\\PowerPlatform\\Dataverse\\models\\index.rst","autoapi\\PowerPlatform\\Dataverse\\models\\labels\\index.rst","autoapi\\PowerPlatform\\Dataverse\\models\\protocol\\index.rst","autoapi\\PowerPlatform\\Dataverse\\models\\query_builder\\index.rst","autoapi\\PowerPlatform\\Dataverse\\models\\record\\index.rst","autoapi\\PowerPlatform\\Dataverse\\models\\relationship\\index.rst","autoapi\\PowerPlatform\\Dataverse\\models\\table_info\\index.rst","autoapi\\PowerPlatform\\Dataverse\\models\\upsert\\index.rst","autoapi\\PowerPlatform\\Dataverse\\operations\\batch\\index.rst","autoapi\\PowerPlatform\\Dataverse\\operations\\dataframe\\index.rst","autoapi\\PowerPlatform\\Dataverse\\operations\\files\\index.rst","autoapi\\PowerPlatform\\Dataverse\\operations\\index.rst","autoapi\\PowerPlatform\\Dataverse\\operations\\query\\index.rst","autoapi\\PowerPlatform\\Dataverse\\operations\\records\\index.rst","autoapi\\PowerPlatform\\Dataverse\\operations\\tables\\index.rst","autoapi\\PowerPlatform\\Dataverse\\utils\\index.rst","autoapi\\PowerPlatform\\index.rst","autoapi\\index.rst","index.rst"],"indexentries":{"add_columns() (powerplatform.dataverse.operations.batch.batchtableoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchTableOperations.add_columns",false]],"add_columns() (powerplatform.dataverse.operations.tables.tableoperations method)":[[30,"PowerPlatform.Dataverse.operations.tables.TableOperations.add_columns",false]],"additional_properties (powerplatform.dataverse.models.labels.label attribute)":[[17,"PowerPlatform.Dataverse.models.labels.Label.additional_properties",false]],"additional_properties (powerplatform.dataverse.models.labels.localizedlabel attribute)":[[17,"PowerPlatform.Dataverse.models.labels.LocalizedLabel.additional_properties",false]],"additional_properties (powerplatform.dataverse.models.relationship.cascadeconfiguration attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.CascadeConfiguration.additional_properties",false]],"additional_properties (powerplatform.dataverse.models.relationship.lookupattributemetadata attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata.additional_properties",false]],"additional_properties (powerplatform.dataverse.models.relationship.manytomanyrelationshipmetadata attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata.additional_properties",false]],"additional_properties (powerplatform.dataverse.models.relationship.onetomanyrelationshipmetadata attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata.additional_properties",false]],"alternate_key (powerplatform.dataverse.models.upsert.upsertitem attribute)":[[23,"PowerPlatform.Dataverse.models.upsert.UpsertItem.alternate_key",false]],"alternatekeyinfo (class in powerplatform.dataverse.models.table_info)":[[22,"PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo",false]],"assign (powerplatform.dataverse.models.relationship.cascadeconfiguration attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.CascadeConfiguration.assign",false]],"auth (powerplatform.dataverse.client.dataverseclient attribute)":[[1,"PowerPlatform.Dataverse.client.DataverseClient.auth",false]],"backup_count (powerplatform.dataverse.core.log_config.logconfig attribute)":[[7,"PowerPlatform.Dataverse.core.log_config.LogConfig.backup_count",false]],"batch (powerplatform.dataverse.client.dataverseclient attribute)":[[1,"PowerPlatform.Dataverse.client.DataverseClient.batch",false]],"batchdataframeoperations (class in powerplatform.dataverse.operations.batch)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchDataFrameOperations",false]],"batchitemresponse (class in powerplatform.dataverse.models.batch)":[[13,"PowerPlatform.Dataverse.models.batch.BatchItemResponse",false]],"batchoperations (class in powerplatform.dataverse.operations.batch)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchOperations",false]],"batchqueryoperations (class in powerplatform.dataverse.operations.batch)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchQueryOperations",false]],"batchrecordoperations (class in powerplatform.dataverse.operations.batch)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchRecordOperations",false]],"batchrequest (class in powerplatform.dataverse.operations.batch)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchRequest",false]],"batchresult (class in powerplatform.dataverse.models.batch)":[[13,"PowerPlatform.Dataverse.models.batch.BatchResult",false]],"batchtableoperations (class in powerplatform.dataverse.operations.batch)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchTableOperations",false]],"between() (in module powerplatform.dataverse.models.filters)":[[15,"PowerPlatform.Dataverse.models.filters.between",false]],"between() (powerplatform.dataverse.models.filters.columnproxy method)":[[15,"PowerPlatform.Dataverse.models.filters.ColumnProxy.between",false]],"builder() (powerplatform.dataverse.operations.query.queryoperations method)":[[28,"PowerPlatform.Dataverse.operations.query.QueryOperations.builder",false]],"cascade_behavior_cascade (in module powerplatform.dataverse.common.constants)":[[2,"PowerPlatform.Dataverse.common.constants.CASCADE_BEHAVIOR_CASCADE",false]],"cascade_behavior_no_cascade (in module powerplatform.dataverse.common.constants)":[[2,"PowerPlatform.Dataverse.common.constants.CASCADE_BEHAVIOR_NO_CASCADE",false]],"cascade_behavior_remove_link (in module powerplatform.dataverse.common.constants)":[[2,"PowerPlatform.Dataverse.common.constants.CASCADE_BEHAVIOR_REMOVE_LINK",false]],"cascade_behavior_restrict (in module powerplatform.dataverse.common.constants)":[[2,"PowerPlatform.Dataverse.common.constants.CASCADE_BEHAVIOR_RESTRICT",false]],"cascade_configuration (powerplatform.dataverse.models.relationship.onetomanyrelationshipmetadata attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata.cascade_configuration",false]],"cascadeconfiguration (class in powerplatform.dataverse.models.relationship)":[[21,"PowerPlatform.Dataverse.models.relationship.CascadeConfiguration",false]],"changeset (class in powerplatform.dataverse.operations.batch)":[[24,"PowerPlatform.Dataverse.operations.batch.ChangeSet",false]],"changeset() (powerplatform.dataverse.operations.batch.batchrequest method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchRequest.changeset",false]],"changesetrecordoperations (class in powerplatform.dataverse.operations.batch)":[[24,"PowerPlatform.Dataverse.operations.batch.ChangeSetRecordOperations",false]],"close() (powerplatform.dataverse.client.dataverseclient method)":[[1,"PowerPlatform.Dataverse.client.DataverseClient.close",false]],"code (powerplatform.dataverse.core.errors.dataverseerror attribute)":[[5,"PowerPlatform.Dataverse.core.errors.DataverseError.code",false]],"col() (in module powerplatform.dataverse.models.filters)":[[15,"PowerPlatform.Dataverse.models.filters.col",false]],"columninfo (class in powerplatform.dataverse.models.table_info)":[[22,"PowerPlatform.Dataverse.models.table_info.ColumnInfo",false]],"columnproxy (class in powerplatform.dataverse.models.filters)":[[15,"PowerPlatform.Dataverse.models.filters.ColumnProxy",false]],"columns (powerplatform.dataverse.models.table_info.tableinfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.TableInfo.columns",false]],"columns_created (powerplatform.dataverse.models.table_info.tableinfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.TableInfo.columns_created",false]],"contains() (in module powerplatform.dataverse.models.filters)":[[15,"PowerPlatform.Dataverse.models.filters.contains",false]],"contains() (powerplatform.dataverse.models.filters.columnproxy method)":[[15,"PowerPlatform.Dataverse.models.filters.ColumnProxy.contains",false]],"content_id (powerplatform.dataverse.models.batch.batchitemresponse attribute)":[[13,"PowerPlatform.Dataverse.models.batch.BatchItemResponse.content_id",false]],"count (powerplatform.dataverse.models.query_builder.queryparams attribute)":[[19,"PowerPlatform.Dataverse.models.query_builder.QueryParams.count",false]],"create() (powerplatform.dataverse.operations.batch.batchdataframeoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchDataFrameOperations.create",false]],"create() (powerplatform.dataverse.operations.batch.batchrecordoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchRecordOperations.create",false]],"create() (powerplatform.dataverse.operations.batch.batchtableoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchTableOperations.create",false]],"create() (powerplatform.dataverse.operations.batch.changesetrecordoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.ChangeSetRecordOperations.create",false]],"create() (powerplatform.dataverse.operations.dataframe.dataframeoperations method)":[[25,"PowerPlatform.Dataverse.operations.dataframe.DataFrameOperations.create",false]],"create() (powerplatform.dataverse.operations.records.recordoperations method)":[[29,"PowerPlatform.Dataverse.operations.records.RecordOperations.create",false]],"create() (powerplatform.dataverse.operations.tables.tableoperations method)":[[30,"PowerPlatform.Dataverse.operations.tables.TableOperations.create",false]],"create_alternate_key() (powerplatform.dataverse.operations.tables.tableoperations method)":[[30,"PowerPlatform.Dataverse.operations.tables.TableOperations.create_alternate_key",false]],"create_lookup_field() (powerplatform.dataverse.operations.batch.batchtableoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchTableOperations.create_lookup_field",false]],"create_lookup_field() (powerplatform.dataverse.operations.tables.tableoperations method)":[[30,"PowerPlatform.Dataverse.operations.tables.TableOperations.create_lookup_field",false]],"create_many_to_many_relationship() (powerplatform.dataverse.operations.batch.batchtableoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchTableOperations.create_many_to_many_relationship",false]],"create_many_to_many_relationship() (powerplatform.dataverse.operations.tables.tableoperations method)":[[30,"PowerPlatform.Dataverse.operations.tables.TableOperations.create_many_to_many_relationship",false]],"create_one_to_many_relationship() (powerplatform.dataverse.operations.batch.batchtableoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchTableOperations.create_one_to_many_relationship",false]],"create_one_to_many_relationship() (powerplatform.dataverse.operations.tables.tableoperations method)":[[30,"PowerPlatform.Dataverse.operations.tables.TableOperations.create_one_to_many_relationship",false]],"data (powerplatform.dataverse.models.batch.batchitemresponse attribute)":[[13,"PowerPlatform.Dataverse.models.batch.BatchItemResponse.data",false]],"data (powerplatform.dataverse.models.record.record attribute)":[[20,"PowerPlatform.Dataverse.models.record.Record.data",false]],"dataframe (powerplatform.dataverse.client.dataverseclient attribute)":[[1,"PowerPlatform.Dataverse.client.DataverseClient.dataframe",false]],"dataframe (powerplatform.dataverse.operations.batch.batchrequest attribute)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchRequest.dataframe",false]],"dataframeoperations (class in powerplatform.dataverse.operations.dataframe)":[[25,"PowerPlatform.Dataverse.operations.dataframe.DataFrameOperations",false]],"dataverseclient (class in powerplatform.dataverse.client)":[[1,"PowerPlatform.Dataverse.client.DataverseClient",false]],"dataverseconfig (class in powerplatform.dataverse.core.config)":[[4,"PowerPlatform.Dataverse.core.config.DataverseConfig",false]],"dataverseerror":[[5,"PowerPlatform.Dataverse.core.errors.DataverseError",false]],"dataversemodel (class in powerplatform.dataverse.models.protocol)":[[18,"PowerPlatform.Dataverse.models.protocol.DataverseModel",false]],"delete (powerplatform.dataverse.models.relationship.cascadeconfiguration attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.CascadeConfiguration.delete",false]],"delete() (powerplatform.dataverse.operations.batch.batchdataframeoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchDataFrameOperations.delete",false]],"delete() (powerplatform.dataverse.operations.batch.batchrecordoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchRecordOperations.delete",false]],"delete() (powerplatform.dataverse.operations.batch.batchtableoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchTableOperations.delete",false]],"delete() (powerplatform.dataverse.operations.batch.changesetrecordoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.ChangeSetRecordOperations.delete",false]],"delete() (powerplatform.dataverse.operations.dataframe.dataframeoperations method)":[[25,"PowerPlatform.Dataverse.operations.dataframe.DataFrameOperations.delete",false]],"delete() (powerplatform.dataverse.operations.records.recordoperations method)":[[29,"PowerPlatform.Dataverse.operations.records.RecordOperations.delete",false]],"delete() (powerplatform.dataverse.operations.tables.tableoperations method)":[[30,"PowerPlatform.Dataverse.operations.tables.TableOperations.delete",false]],"delete_alternate_key() (powerplatform.dataverse.operations.tables.tableoperations method)":[[30,"PowerPlatform.Dataverse.operations.tables.TableOperations.delete_alternate_key",false]],"delete_relationship() (powerplatform.dataverse.operations.batch.batchtableoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchTableOperations.delete_relationship",false]],"delete_relationship() (powerplatform.dataverse.operations.tables.tableoperations method)":[[30,"PowerPlatform.Dataverse.operations.tables.TableOperations.delete_relationship",false]],"description (powerplatform.dataverse.models.relationship.lookupattributemetadata attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata.description",false]],"description (powerplatform.dataverse.models.table_info.columninfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.ColumnInfo.description",false]],"description (powerplatform.dataverse.models.table_info.tableinfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.TableInfo.description",false]],"details (powerplatform.dataverse.core.errors.dataverseerror attribute)":[[5,"PowerPlatform.Dataverse.core.errors.DataverseError.details",false]],"display_name (powerplatform.dataverse.models.relationship.lookupattributemetadata attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata.display_name",false]],"display_name (powerplatform.dataverse.models.table_info.columninfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.ColumnInfo.display_name",false]],"display_name (powerplatform.dataverse.models.table_info.tableinfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.TableInfo.display_name",false]],"endswith() (in module powerplatform.dataverse.models.filters)":[[15,"PowerPlatform.Dataverse.models.filters.endswith",false]],"endswith() (powerplatform.dataverse.models.filters.columnproxy method)":[[15,"PowerPlatform.Dataverse.models.filters.ColumnProxy.endswith",false]],"entity1_logical_name (powerplatform.dataverse.models.relationship.manytomanyrelationshipmetadata attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata.entity1_logical_name",false]],"entity1_logical_name (powerplatform.dataverse.models.relationship.relationshipinfo attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.RelationshipInfo.entity1_logical_name",false]],"entity2_logical_name (powerplatform.dataverse.models.relationship.manytomanyrelationshipmetadata attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata.entity2_logical_name",false]],"entity2_logical_name (powerplatform.dataverse.models.relationship.relationshipinfo attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.RelationshipInfo.entity2_logical_name",false]],"entity_id (powerplatform.dataverse.models.batch.batchitemresponse attribute)":[[13,"PowerPlatform.Dataverse.models.batch.BatchItemResponse.entity_id",false]],"entity_ids (powerplatform.dataverse.models.batch.batchresult property)":[[13,"PowerPlatform.Dataverse.models.batch.BatchResult.entity_ids",false]],"entity_set_name (powerplatform.dataverse.models.table_info.tableinfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.TableInfo.entity_set_name",false]],"eq() (in module powerplatform.dataverse.models.filters)":[[15,"PowerPlatform.Dataverse.models.filters.eq",false]],"error_code (powerplatform.dataverse.models.batch.batchitemresponse attribute)":[[13,"PowerPlatform.Dataverse.models.batch.BatchItemResponse.error_code",false]],"error_message (powerplatform.dataverse.models.batch.batchitemresponse attribute)":[[13,"PowerPlatform.Dataverse.models.batch.BatchItemResponse.error_message",false]],"etag (powerplatform.dataverse.models.record.record attribute)":[[20,"PowerPlatform.Dataverse.models.record.Record.etag",false]],"execute() (powerplatform.dataverse.models.fetchxml_query.fetchxmlquery method)":[[14,"PowerPlatform.Dataverse.models.fetchxml_query.FetchXmlQuery.execute",false]],"execute() (powerplatform.dataverse.models.query_builder.querybuilder method)":[[19,"PowerPlatform.Dataverse.models.query_builder.QueryBuilder.execute",false]],"execute() (powerplatform.dataverse.operations.batch.batchrequest method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchRequest.execute",false]],"execute_pages() (powerplatform.dataverse.models.fetchxml_query.fetchxmlquery method)":[[14,"PowerPlatform.Dataverse.models.fetchxml_query.FetchXmlQuery.execute_pages",false]],"execute_pages() (powerplatform.dataverse.models.query_builder.querybuilder method)":[[19,"PowerPlatform.Dataverse.models.query_builder.QueryBuilder.execute_pages",false]],"expand (powerplatform.dataverse.models.query_builder.queryparams attribute)":[[19,"PowerPlatform.Dataverse.models.query_builder.QueryParams.expand",false]],"expandoption (class in powerplatform.dataverse.models.query_builder)":[[19,"PowerPlatform.Dataverse.models.query_builder.ExpandOption",false]],"failed (powerplatform.dataverse.models.batch.batchresult property)":[[13,"PowerPlatform.Dataverse.models.batch.BatchResult.failed",false]],"fetchxml() (powerplatform.dataverse.operations.query.queryoperations method)":[[28,"PowerPlatform.Dataverse.operations.query.QueryOperations.fetchxml",false]],"fetchxmlquery (class in powerplatform.dataverse.models.fetchxml_query)":[[14,"PowerPlatform.Dataverse.models.fetchxml_query.FetchXmlQuery",false]],"fileoperations (class in powerplatform.dataverse.operations.files)":[[26,"PowerPlatform.Dataverse.operations.files.FileOperations",false]],"files (powerplatform.dataverse.client.dataverseclient attribute)":[[1,"PowerPlatform.Dataverse.client.DataverseClient.files",false]],"filter (powerplatform.dataverse.models.query_builder.queryparams attribute)":[[19,"PowerPlatform.Dataverse.models.query_builder.QueryParams.filter",false]],"filter() (powerplatform.dataverse.models.query_builder.expandoption method)":[[19,"PowerPlatform.Dataverse.models.query_builder.ExpandOption.filter",false]],"filter_in() (in module powerplatform.dataverse.models.filters)":[[15,"PowerPlatform.Dataverse.models.filters.filter_in",false]],"filterexpression (class in powerplatform.dataverse.models.filters)":[[15,"PowerPlatform.Dataverse.models.filters.FilterExpression",false]],"find_manual_patterns() (in module powerplatform.dataverse.migration.migrate_v0_to_v1)":[[12,"PowerPlatform.Dataverse.migration.migrate_v0_to_v1.find_manual_patterns",false]],"first() (powerplatform.dataverse.models.record.queryresult method)":[[20,"PowerPlatform.Dataverse.models.record.QueryResult.first",false]],"flush_cache() (powerplatform.dataverse.client.dataverseclient method)":[[1,"PowerPlatform.Dataverse.client.DataverseClient.flush_cache",false]],"from_api_response() (powerplatform.dataverse.models.record.record class method)":[[20,"PowerPlatform.Dataverse.models.record.Record.from_api_response",false]],"from_api_response() (powerplatform.dataverse.models.relationship.relationshipinfo class method)":[[21,"PowerPlatform.Dataverse.models.relationship.RelationshipInfo.from_api_response",false]],"from_api_response() (powerplatform.dataverse.models.table_info.alternatekeyinfo class method)":[[22,"PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo.from_api_response",false]],"from_api_response() (powerplatform.dataverse.models.table_info.columninfo class method)":[[22,"PowerPlatform.Dataverse.models.table_info.ColumnInfo.from_api_response",false]],"from_api_response() (powerplatform.dataverse.models.table_info.tableinfo class method)":[[22,"PowerPlatform.Dataverse.models.table_info.TableInfo.from_api_response",false]],"from_dict() (powerplatform.dataverse.models.protocol.dataversemodel class method)":[[18,"PowerPlatform.Dataverse.models.protocol.DataverseModel.from_dict",false]],"from_dict() (powerplatform.dataverse.models.table_info.tableinfo class method)":[[22,"PowerPlatform.Dataverse.models.table_info.TableInfo.from_dict",false]],"from_env() (powerplatform.dataverse.core.config.dataverseconfig class method)":[[4,"PowerPlatform.Dataverse.core.config.DataverseConfig.from_env",false]],"from_many_to_many() (powerplatform.dataverse.models.relationship.relationshipinfo class method)":[[21,"PowerPlatform.Dataverse.models.relationship.RelationshipInfo.from_many_to_many",false]],"from_one_to_many() (powerplatform.dataverse.models.relationship.relationshipinfo class method)":[[21,"PowerPlatform.Dataverse.models.relationship.RelationshipInfo.from_one_to_many",false]],"ge() (in module powerplatform.dataverse.models.filters)":[[15,"PowerPlatform.Dataverse.models.filters.ge",false]],"get() (powerplatform.dataverse.models.record.record method)":[[20,"PowerPlatform.Dataverse.models.record.Record.get",false]],"get() (powerplatform.dataverse.models.table_info.tableinfo method)":[[22,"PowerPlatform.Dataverse.models.table_info.TableInfo.get",false]],"get() (powerplatform.dataverse.operations.batch.batchrecordoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchRecordOperations.get",false]],"get() (powerplatform.dataverse.operations.batch.batchtableoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchTableOperations.get",false]],"get() (powerplatform.dataverse.operations.dataframe.dataframeoperations method)":[[25,"PowerPlatform.Dataverse.operations.dataframe.DataFrameOperations.get",false]],"get() (powerplatform.dataverse.operations.records.recordoperations method)":[[29,"PowerPlatform.Dataverse.operations.records.RecordOperations.get",false]],"get() (powerplatform.dataverse.operations.tables.tableoperations method)":[[30,"PowerPlatform.Dataverse.operations.tables.TableOperations.get",false]],"get_alternate_keys() (powerplatform.dataverse.operations.tables.tableoperations method)":[[30,"PowerPlatform.Dataverse.operations.tables.TableOperations.get_alternate_keys",false]],"get_relationship() (powerplatform.dataverse.operations.batch.batchtableoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchTableOperations.get_relationship",false]],"get_relationship() (powerplatform.dataverse.operations.tables.tableoperations method)":[[30,"PowerPlatform.Dataverse.operations.tables.TableOperations.get_relationship",false]],"gt() (in module powerplatform.dataverse.models.filters)":[[15,"PowerPlatform.Dataverse.models.filters.gt",false]],"has_errors (powerplatform.dataverse.models.batch.batchresult property)":[[13,"PowerPlatform.Dataverse.models.batch.BatchResult.has_errors",false]],"http_backoff (powerplatform.dataverse.core.config.dataverseconfig attribute)":[[4,"PowerPlatform.Dataverse.core.config.DataverseConfig.http_backoff",false]],"http_retries (powerplatform.dataverse.core.config.dataverseconfig attribute)":[[4,"PowerPlatform.Dataverse.core.config.DataverseConfig.http_retries",false]],"http_timeout (powerplatform.dataverse.core.config.dataverseconfig attribute)":[[4,"PowerPlatform.Dataverse.core.config.DataverseConfig.http_timeout",false]],"httperror":[[5,"PowerPlatform.Dataverse.core.errors.HttpError",false]],"id (powerplatform.dataverse.models.record.record attribute)":[[20,"PowerPlatform.Dataverse.models.record.Record.id",false]],"in_() (powerplatform.dataverse.models.filters.columnproxy method)":[[15,"PowerPlatform.Dataverse.models.filters.ColumnProxy.in_",false]],"include_annotations (powerplatform.dataverse.models.query_builder.queryparams attribute)":[[19,"PowerPlatform.Dataverse.models.query_builder.QueryParams.include_annotations",false]],"intersect_entity_name (powerplatform.dataverse.models.relationship.manytomanyrelationshipmetadata attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata.intersect_entity_name",false]],"is_not_null() (in module powerplatform.dataverse.models.filters)":[[15,"PowerPlatform.Dataverse.models.filters.is_not_null",false]],"is_not_null() (powerplatform.dataverse.models.filters.columnproxy method)":[[15,"PowerPlatform.Dataverse.models.filters.ColumnProxy.is_not_null",false]],"is_null() (in module powerplatform.dataverse.models.filters)":[[15,"PowerPlatform.Dataverse.models.filters.is_null",false]],"is_null() (powerplatform.dataverse.models.filters.columnproxy method)":[[15,"PowerPlatform.Dataverse.models.filters.ColumnProxy.is_null",false]],"is_primary (powerplatform.dataverse.models.table_info.columninfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.ColumnInfo.is_primary",false]],"is_required (powerplatform.dataverse.models.table_info.columninfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.ColumnInfo.is_required",false]],"is_success (powerplatform.dataverse.models.batch.batchitemresponse property)":[[13,"PowerPlatform.Dataverse.models.batch.BatchItemResponse.is_success",false]],"is_transient (powerplatform.dataverse.core.errors.dataverseerror attribute)":[[5,"PowerPlatform.Dataverse.core.errors.DataverseError.is_transient",false]],"items() (powerplatform.dataverse.models.record.record method)":[[20,"PowerPlatform.Dataverse.models.record.Record.items",false]],"items() (powerplatform.dataverse.models.table_info.tableinfo method)":[[22,"PowerPlatform.Dataverse.models.table_info.TableInfo.items",false]],"key_attributes (powerplatform.dataverse.models.table_info.alternatekeyinfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo.key_attributes",false]],"keys() (powerplatform.dataverse.models.record.record method)":[[20,"PowerPlatform.Dataverse.models.record.Record.keys",false]],"keys() (powerplatform.dataverse.models.table_info.tableinfo method)":[[22,"PowerPlatform.Dataverse.models.table_info.TableInfo.keys",false]],"label (class in powerplatform.dataverse.models.labels)":[[17,"PowerPlatform.Dataverse.models.labels.Label",false]],"label (powerplatform.dataverse.models.labels.localizedlabel attribute)":[[17,"PowerPlatform.Dataverse.models.labels.LocalizedLabel.label",false]],"language_code (powerplatform.dataverse.core.config.dataverseconfig attribute)":[[4,"PowerPlatform.Dataverse.core.config.DataverseConfig.language_code",false]],"language_code (powerplatform.dataverse.models.labels.localizedlabel attribute)":[[17,"PowerPlatform.Dataverse.models.labels.LocalizedLabel.language_code",false]],"le() (in module powerplatform.dataverse.models.filters)":[[15,"PowerPlatform.Dataverse.models.filters.le",false]],"like() (powerplatform.dataverse.models.filters.columnproxy method)":[[15,"PowerPlatform.Dataverse.models.filters.ColumnProxy.like",false]],"list() (powerplatform.dataverse.operations.batch.batchrecordoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchRecordOperations.list",false]],"list() (powerplatform.dataverse.operations.batch.batchtableoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchTableOperations.list",false]],"list() (powerplatform.dataverse.operations.records.recordoperations method)":[[29,"PowerPlatform.Dataverse.operations.records.RecordOperations.list",false]],"list() (powerplatform.dataverse.operations.tables.tableoperations method)":[[30,"PowerPlatform.Dataverse.operations.tables.TableOperations.list",false]],"list_columns() (powerplatform.dataverse.operations.tables.tableoperations method)":[[30,"PowerPlatform.Dataverse.operations.tables.TableOperations.list_columns",false]],"list_pages() (powerplatform.dataverse.operations.records.recordoperations method)":[[29,"PowerPlatform.Dataverse.operations.records.RecordOperations.list_pages",false]],"list_relationships() (powerplatform.dataverse.operations.tables.tableoperations method)":[[30,"PowerPlatform.Dataverse.operations.tables.TableOperations.list_relationships",false]],"list_table_relationships() (powerplatform.dataverse.operations.tables.tableoperations method)":[[30,"PowerPlatform.Dataverse.operations.tables.TableOperations.list_table_relationships",false]],"localized_labels (powerplatform.dataverse.models.labels.label attribute)":[[17,"PowerPlatform.Dataverse.models.labels.Label.localized_labels",false]],"localizedlabel (class in powerplatform.dataverse.models.labels)":[[17,"PowerPlatform.Dataverse.models.labels.LocalizedLabel",false]],"log_config (powerplatform.dataverse.core.config.dataverseconfig attribute)":[[4,"PowerPlatform.Dataverse.core.config.DataverseConfig.log_config",false]],"log_file_prefix (powerplatform.dataverse.core.log_config.logconfig attribute)":[[7,"PowerPlatform.Dataverse.core.log_config.LogConfig.log_file_prefix",false]],"log_folder (powerplatform.dataverse.core.log_config.logconfig attribute)":[[7,"PowerPlatform.Dataverse.core.log_config.LogConfig.log_folder",false]],"log_level (powerplatform.dataverse.core.log_config.logconfig attribute)":[[7,"PowerPlatform.Dataverse.core.log_config.LogConfig.log_level",false]],"logconfig (class in powerplatform.dataverse.core.log_config)":[[7,"PowerPlatform.Dataverse.core.log_config.LogConfig",false]],"logical_name (powerplatform.dataverse.models.table_info.columninfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.ColumnInfo.logical_name",false]],"logical_name (powerplatform.dataverse.models.table_info.tableinfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.TableInfo.logical_name",false]],"lookup_schema_name (powerplatform.dataverse.models.relationship.relationshipinfo attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.RelationshipInfo.lookup_schema_name",false]],"lookupattributemetadata (class in powerplatform.dataverse.models.relationship)":[[21,"PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata",false]],"lt() (in module powerplatform.dataverse.models.filters)":[[15,"PowerPlatform.Dataverse.models.filters.lt",false]],"main() (in module powerplatform.dataverse.migration.migrate_v0_to_v1)":[[12,"PowerPlatform.Dataverse.migration.migrate_v0_to_v1.main",false]],"manytomanyrelationshipmetadata (class in powerplatform.dataverse.models.relationship)":[[21,"PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata",false]],"max_body_bytes (powerplatform.dataverse.core.log_config.logconfig attribute)":[[7,"PowerPlatform.Dataverse.core.log_config.LogConfig.max_body_bytes",false]],"max_file_bytes (powerplatform.dataverse.core.log_config.logconfig attribute)":[[7,"PowerPlatform.Dataverse.core.log_config.LogConfig.max_file_bytes",false]],"max_length (powerplatform.dataverse.models.table_info.columninfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.ColumnInfo.max_length",false]],"merge (powerplatform.dataverse.models.relationship.cascadeconfiguration attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.CascadeConfiguration.merge",false]],"message (powerplatform.dataverse.core.errors.dataverseerror attribute)":[[5,"PowerPlatform.Dataverse.core.errors.DataverseError.message",false]],"metadata_id (powerplatform.dataverse.models.table_info.alternatekeyinfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo.metadata_id",false]],"metadata_id (powerplatform.dataverse.models.table_info.tableinfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.TableInfo.metadata_id",false]],"metadataerror":[[5,"PowerPlatform.Dataverse.core.errors.MetadataError",false]],"migrate_file() (in module powerplatform.dataverse.migration.migrate_v0_to_v1)":[[12,"PowerPlatform.Dataverse.migration.migrate_v0_to_v1.migrate_file",false]],"migrate_source() (in module powerplatform.dataverse.migration.migrate_v0_to_v1)":[[12,"PowerPlatform.Dataverse.migration.migrate_v0_to_v1.migrate_source",false]],"module":[[0,"module-PowerPlatform.Dataverse.claude_skill",false],[1,"module-PowerPlatform.Dataverse.client",false],[2,"module-PowerPlatform.Dataverse.common.constants",false],[3,"module-PowerPlatform.Dataverse.common",false],[4,"module-PowerPlatform.Dataverse.core.config",false],[5,"module-PowerPlatform.Dataverse.core.errors",false],[6,"module-PowerPlatform.Dataverse.core",false],[7,"module-PowerPlatform.Dataverse.core.log_config",false],[8,"module-PowerPlatform.Dataverse.data",false],[9,"module-PowerPlatform.Dataverse.extensions",false],[10,"module-PowerPlatform.Dataverse",false],[11,"module-PowerPlatform.Dataverse.migration",false],[12,"module-PowerPlatform.Dataverse.migration.migrate_v0_to_v1",false],[13,"module-PowerPlatform.Dataverse.models.batch",false],[14,"module-PowerPlatform.Dataverse.models.fetchxml_query",false],[15,"module-PowerPlatform.Dataverse.models.filters",false],[16,"module-PowerPlatform.Dataverse.models",false],[17,"module-PowerPlatform.Dataverse.models.labels",false],[18,"module-PowerPlatform.Dataverse.models.protocol",false],[19,"module-PowerPlatform.Dataverse.models.query_builder",false],[20,"module-PowerPlatform.Dataverse.models.record",false],[21,"module-PowerPlatform.Dataverse.models.relationship",false],[22,"module-PowerPlatform.Dataverse.models.table_info",false],[23,"module-PowerPlatform.Dataverse.models.upsert",false],[24,"module-PowerPlatform.Dataverse.operations.batch",false],[25,"module-PowerPlatform.Dataverse.operations.dataframe",false],[26,"module-PowerPlatform.Dataverse.operations.files",false],[27,"module-PowerPlatform.Dataverse.operations",false],[28,"module-PowerPlatform.Dataverse.operations.query",false],[29,"module-PowerPlatform.Dataverse.operations.records",false],[30,"module-PowerPlatform.Dataverse.operations.tables",false],[31,"module-PowerPlatform.Dataverse.utils",false],[32,"module-PowerPlatform",false]],"ne() (in module powerplatform.dataverse.models.filters)":[[15,"PowerPlatform.Dataverse.models.filters.ne",false]],"new() (powerplatform.dataverse.operations.batch.batchoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchOperations.new",false]],"not_between() (in module powerplatform.dataverse.models.filters)":[[15,"PowerPlatform.Dataverse.models.filters.not_between",false]],"not_between() (powerplatform.dataverse.models.filters.columnproxy method)":[[15,"PowerPlatform.Dataverse.models.filters.ColumnProxy.not_between",false]],"not_in() (in module powerplatform.dataverse.models.filters)":[[15,"PowerPlatform.Dataverse.models.filters.not_in",false]],"not_in() (powerplatform.dataverse.models.filters.columnproxy method)":[[15,"PowerPlatform.Dataverse.models.filters.ColumnProxy.not_in",false]],"not_like() (powerplatform.dataverse.models.filters.columnproxy method)":[[15,"PowerPlatform.Dataverse.models.filters.ColumnProxy.not_like",false]],"odata_bind() (powerplatform.dataverse.operations.query.queryoperations method)":[[28,"PowerPlatform.Dataverse.operations.query.QueryOperations.odata_bind",false]],"odata_expand() (powerplatform.dataverse.operations.query.queryoperations method)":[[28,"PowerPlatform.Dataverse.operations.query.QueryOperations.odata_expand",false]],"odata_expands() (powerplatform.dataverse.operations.query.queryoperations method)":[[28,"PowerPlatform.Dataverse.operations.query.QueryOperations.odata_expands",false]],"odata_select() (powerplatform.dataverse.operations.query.queryoperations method)":[[28,"PowerPlatform.Dataverse.operations.query.QueryOperations.odata_select",false]],"odata_type_label (in module powerplatform.dataverse.common.constants)":[[2,"PowerPlatform.Dataverse.common.constants.ODATA_TYPE_LABEL",false]],"odata_type_localized_label (in module powerplatform.dataverse.common.constants)":[[2,"PowerPlatform.Dataverse.common.constants.ODATA_TYPE_LOCALIZED_LABEL",false]],"odata_type_lookup_attribute (in module powerplatform.dataverse.common.constants)":[[2,"PowerPlatform.Dataverse.common.constants.ODATA_TYPE_LOOKUP_ATTRIBUTE",false]],"odata_type_many_to_many_relationship (in module powerplatform.dataverse.common.constants)":[[2,"PowerPlatform.Dataverse.common.constants.ODATA_TYPE_MANY_TO_MANY_RELATIONSHIP",false]],"odata_type_one_to_many_relationship (in module powerplatform.dataverse.common.constants)":[[2,"PowerPlatform.Dataverse.common.constants.ODATA_TYPE_ONE_TO_MANY_RELATIONSHIP",false]],"onetomanyrelationshipmetadata (class in powerplatform.dataverse.models.relationship)":[[21,"PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata",false]],"operation_context (powerplatform.dataverse.core.config.dataverseconfig attribute)":[[4,"PowerPlatform.Dataverse.core.config.DataverseConfig.operation_context",false]],"operationcontext (class in powerplatform.dataverse.core.config)":[[4,"PowerPlatform.Dataverse.core.config.OperationContext",false]],"order_by() (powerplatform.dataverse.models.query_builder.expandoption method)":[[19,"PowerPlatform.Dataverse.models.query_builder.ExpandOption.order_by",false]],"orderby (powerplatform.dataverse.models.query_builder.queryparams attribute)":[[19,"PowerPlatform.Dataverse.models.query_builder.QueryParams.orderby",false]],"page_size (powerplatform.dataverse.models.query_builder.queryparams attribute)":[[19,"PowerPlatform.Dataverse.models.query_builder.QueryParams.page_size",false]],"powerplatform":[[32,"module-PowerPlatform",false]],"powerplatform.dataverse":[[10,"module-PowerPlatform.Dataverse",false]],"powerplatform.dataverse.claude_skill":[[0,"module-PowerPlatform.Dataverse.claude_skill",false]],"powerplatform.dataverse.client":[[1,"module-PowerPlatform.Dataverse.client",false]],"powerplatform.dataverse.common":[[3,"module-PowerPlatform.Dataverse.common",false]],"powerplatform.dataverse.common.constants":[[2,"module-PowerPlatform.Dataverse.common.constants",false]],"powerplatform.dataverse.core":[[6,"module-PowerPlatform.Dataverse.core",false]],"powerplatform.dataverse.core.config":[[4,"module-PowerPlatform.Dataverse.core.config",false]],"powerplatform.dataverse.core.errors":[[5,"module-PowerPlatform.Dataverse.core.errors",false]],"powerplatform.dataverse.core.log_config":[[7,"module-PowerPlatform.Dataverse.core.log_config",false]],"powerplatform.dataverse.data":[[8,"module-PowerPlatform.Dataverse.data",false]],"powerplatform.dataverse.extensions":[[9,"module-PowerPlatform.Dataverse.extensions",false]],"powerplatform.dataverse.migration":[[11,"module-PowerPlatform.Dataverse.migration",false]],"powerplatform.dataverse.migration.migrate_v0_to_v1":[[12,"module-PowerPlatform.Dataverse.migration.migrate_v0_to_v1",false]],"powerplatform.dataverse.models":[[16,"module-PowerPlatform.Dataverse.models",false]],"powerplatform.dataverse.models.batch":[[13,"module-PowerPlatform.Dataverse.models.batch",false]],"powerplatform.dataverse.models.fetchxml_query":[[14,"module-PowerPlatform.Dataverse.models.fetchxml_query",false]],"powerplatform.dataverse.models.filters":[[15,"module-PowerPlatform.Dataverse.models.filters",false]],"powerplatform.dataverse.models.labels":[[17,"module-PowerPlatform.Dataverse.models.labels",false]],"powerplatform.dataverse.models.protocol":[[18,"module-PowerPlatform.Dataverse.models.protocol",false]],"powerplatform.dataverse.models.query_builder":[[19,"module-PowerPlatform.Dataverse.models.query_builder",false]],"powerplatform.dataverse.models.record":[[20,"module-PowerPlatform.Dataverse.models.record",false]],"powerplatform.dataverse.models.relationship":[[21,"module-PowerPlatform.Dataverse.models.relationship",false]],"powerplatform.dataverse.models.table_info":[[22,"module-PowerPlatform.Dataverse.models.table_info",false]],"powerplatform.dataverse.models.upsert":[[23,"module-PowerPlatform.Dataverse.models.upsert",false]],"powerplatform.dataverse.operations":[[27,"module-PowerPlatform.Dataverse.operations",false]],"powerplatform.dataverse.operations.batch":[[24,"module-PowerPlatform.Dataverse.operations.batch",false]],"powerplatform.dataverse.operations.dataframe":[[25,"module-PowerPlatform.Dataverse.operations.dataframe",false]],"powerplatform.dataverse.operations.files":[[26,"module-PowerPlatform.Dataverse.operations.files",false]],"powerplatform.dataverse.operations.query":[[28,"module-PowerPlatform.Dataverse.operations.query",false]],"powerplatform.dataverse.operations.records":[[29,"module-PowerPlatform.Dataverse.operations.records",false]],"powerplatform.dataverse.operations.tables":[[30,"module-PowerPlatform.Dataverse.operations.tables",false]],"powerplatform.dataverse.utils":[[31,"module-PowerPlatform.Dataverse.utils",false]],"primary_id_attribute (powerplatform.dataverse.models.table_info.tableinfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.TableInfo.primary_id_attribute",false]],"primary_name_attribute (powerplatform.dataverse.models.table_info.tableinfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.TableInfo.primary_name_attribute",false]],"query (powerplatform.dataverse.client.dataverseclient attribute)":[[1,"PowerPlatform.Dataverse.client.DataverseClient.query",false]],"query (powerplatform.dataverse.operations.batch.batchrequest attribute)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchRequest.query",false]],"querybuilder (class in powerplatform.dataverse.models.query_builder)":[[19,"PowerPlatform.Dataverse.models.query_builder.QueryBuilder",false]],"queryoperations (class in powerplatform.dataverse.operations.query)":[[28,"PowerPlatform.Dataverse.operations.query.QueryOperations",false]],"queryparams (class in powerplatform.dataverse.models.query_builder)":[[19,"PowerPlatform.Dataverse.models.query_builder.QueryParams",false]],"queryresult (class in powerplatform.dataverse.models.record)":[[20,"PowerPlatform.Dataverse.models.record.QueryResult",false]],"raw() (in module powerplatform.dataverse.models.filters)":[[15,"PowerPlatform.Dataverse.models.filters.raw",false]],"record (class in powerplatform.dataverse.models.record)":[[20,"PowerPlatform.Dataverse.models.record.Record",false]],"record (powerplatform.dataverse.models.upsert.upsertitem attribute)":[[23,"PowerPlatform.Dataverse.models.upsert.UpsertItem.record",false]],"recordoperations (class in powerplatform.dataverse.operations.records)":[[29,"PowerPlatform.Dataverse.operations.records.RecordOperations",false]],"records (powerplatform.dataverse.client.dataverseclient attribute)":[[1,"PowerPlatform.Dataverse.client.DataverseClient.records",false]],"records (powerplatform.dataverse.models.record.queryresult attribute)":[[20,"PowerPlatform.Dataverse.models.record.QueryResult.records",false]],"records (powerplatform.dataverse.operations.batch.batchrequest attribute)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchRequest.records",false]],"records (powerplatform.dataverse.operations.batch.changeset attribute)":[[24,"PowerPlatform.Dataverse.operations.batch.ChangeSet.records",false]],"redacted_headers (powerplatform.dataverse.core.log_config.logconfig attribute)":[[7,"PowerPlatform.Dataverse.core.log_config.LogConfig.redacted_headers",false]],"referenced_attribute (powerplatform.dataverse.models.relationship.onetomanyrelationshipmetadata attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata.referenced_attribute",false]],"referenced_entity (powerplatform.dataverse.models.relationship.onetomanyrelationshipmetadata attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata.referenced_entity",false]],"referenced_entity (powerplatform.dataverse.models.relationship.relationshipinfo attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.RelationshipInfo.referenced_entity",false]],"referencing_attribute (powerplatform.dataverse.models.relationship.onetomanyrelationshipmetadata attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata.referencing_attribute",false]],"referencing_entity (powerplatform.dataverse.models.relationship.onetomanyrelationshipmetadata attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata.referencing_entity",false]],"referencing_entity (powerplatform.dataverse.models.relationship.relationshipinfo attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.RelationshipInfo.referencing_entity",false]],"relation (powerplatform.dataverse.models.query_builder.expandoption attribute)":[[19,"PowerPlatform.Dataverse.models.query_builder.ExpandOption.relation",false]],"relationship_id (powerplatform.dataverse.models.relationship.relationshipinfo attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.RelationshipInfo.relationship_id",false]],"relationship_schema_name (powerplatform.dataverse.models.relationship.relationshipinfo attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.RelationshipInfo.relationship_schema_name",false]],"relationship_type (powerplatform.dataverse.models.relationship.relationshipinfo attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.RelationshipInfo.relationship_type",false]],"relationshipinfo (class in powerplatform.dataverse.models.relationship)":[[21,"PowerPlatform.Dataverse.models.relationship.RelationshipInfo",false]],"remove_columns() (powerplatform.dataverse.operations.batch.batchtableoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchTableOperations.remove_columns",false]],"remove_columns() (powerplatform.dataverse.operations.tables.tableoperations method)":[[30,"PowerPlatform.Dataverse.operations.tables.TableOperations.remove_columns",false]],"reparent (powerplatform.dataverse.models.relationship.cascadeconfiguration attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.CascadeConfiguration.reparent",false]],"required_level (powerplatform.dataverse.models.relationship.lookupattributemetadata attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata.required_level",false]],"responses (powerplatform.dataverse.models.batch.batchresult attribute)":[[13,"PowerPlatform.Dataverse.models.batch.BatchResult.responses",false]],"retrieve() (powerplatform.dataverse.operations.batch.batchrecordoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchRecordOperations.retrieve",false]],"retrieve() (powerplatform.dataverse.operations.records.recordoperations method)":[[29,"PowerPlatform.Dataverse.operations.records.RecordOperations.retrieve",false]],"schema_name (powerplatform.dataverse.models.relationship.lookupattributemetadata attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata.schema_name",false]],"schema_name (powerplatform.dataverse.models.relationship.manytomanyrelationshipmetadata attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata.schema_name",false]],"schema_name (powerplatform.dataverse.models.relationship.onetomanyrelationshipmetadata attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata.schema_name",false]],"schema_name (powerplatform.dataverse.models.table_info.alternatekeyinfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo.schema_name",false]],"schema_name (powerplatform.dataverse.models.table_info.columninfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.ColumnInfo.schema_name",false]],"schema_name (powerplatform.dataverse.models.table_info.tableinfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.TableInfo.schema_name",false]],"select (powerplatform.dataverse.models.query_builder.queryparams attribute)":[[19,"PowerPlatform.Dataverse.models.query_builder.QueryParams.select",false]],"select() (powerplatform.dataverse.models.query_builder.expandoption method)":[[19,"PowerPlatform.Dataverse.models.query_builder.ExpandOption.select",false]],"share (powerplatform.dataverse.models.relationship.cascadeconfiguration attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.CascadeConfiguration.share",false]],"source (powerplatform.dataverse.core.errors.dataverseerror attribute)":[[5,"PowerPlatform.Dataverse.core.errors.DataverseError.source",false]],"sql() (powerplatform.dataverse.operations.batch.batchqueryoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchQueryOperations.sql",false]],"sql() (powerplatform.dataverse.operations.dataframe.dataframeoperations method)":[[25,"PowerPlatform.Dataverse.operations.dataframe.DataFrameOperations.sql",false]],"sql() (powerplatform.dataverse.operations.query.queryoperations method)":[[28,"PowerPlatform.Dataverse.operations.query.QueryOperations.sql",false]],"sql_columns() (powerplatform.dataverse.operations.query.queryoperations method)":[[28,"PowerPlatform.Dataverse.operations.query.QueryOperations.sql_columns",false]],"sqlparseerror":[[5,"PowerPlatform.Dataverse.core.errors.SQLParseError",false]],"startswith() (in module powerplatform.dataverse.models.filters)":[[15,"PowerPlatform.Dataverse.models.filters.startswith",false]],"startswith() (powerplatform.dataverse.models.filters.columnproxy method)":[[15,"PowerPlatform.Dataverse.models.filters.ColumnProxy.startswith",false]],"status (powerplatform.dataverse.models.table_info.alternatekeyinfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo.status",false]],"status_code (powerplatform.dataverse.core.errors.dataverseerror attribute)":[[5,"PowerPlatform.Dataverse.core.errors.DataverseError.status_code",false]],"status_code (powerplatform.dataverse.models.batch.batchitemresponse attribute)":[[13,"PowerPlatform.Dataverse.models.batch.BatchItemResponse.status_code",false]],"subcode (powerplatform.dataverse.core.errors.dataverseerror attribute)":[[5,"PowerPlatform.Dataverse.core.errors.DataverseError.subcode",false]],"succeeded (powerplatform.dataverse.models.batch.batchresult property)":[[13,"PowerPlatform.Dataverse.models.batch.BatchResult.succeeded",false]],"table (powerplatform.dataverse.models.query_builder.queryparams attribute)":[[19,"PowerPlatform.Dataverse.models.query_builder.QueryParams.table",false]],"table (powerplatform.dataverse.models.record.record attribute)":[[20,"PowerPlatform.Dataverse.models.record.Record.table",false]],"tableinfo (class in powerplatform.dataverse.models.table_info)":[[22,"PowerPlatform.Dataverse.models.table_info.TableInfo",false]],"tableoperations (class in powerplatform.dataverse.operations.tables)":[[30,"PowerPlatform.Dataverse.operations.tables.TableOperations",false]],"tables (powerplatform.dataverse.client.dataverseclient attribute)":[[1,"PowerPlatform.Dataverse.client.DataverseClient.tables",false]],"tables (powerplatform.dataverse.operations.batch.batchrequest attribute)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchRequest.tables",false]],"timestamp (powerplatform.dataverse.core.errors.dataverseerror attribute)":[[5,"PowerPlatform.Dataverse.core.errors.DataverseError.timestamp",false]],"to_dataframe() (powerplatform.dataverse.models.query_builder.querybuilder method)":[[19,"PowerPlatform.Dataverse.models.query_builder.QueryBuilder.to_dataframe",false]],"to_dataframe() (powerplatform.dataverse.models.record.queryresult method)":[[20,"PowerPlatform.Dataverse.models.record.QueryResult.to_dataframe",false]],"to_dict() (powerplatform.dataverse.core.errors.dataverseerror method)":[[5,"PowerPlatform.Dataverse.core.errors.DataverseError.to_dict",false]],"to_dict() (powerplatform.dataverse.models.labels.label method)":[[17,"PowerPlatform.Dataverse.models.labels.Label.to_dict",false]],"to_dict() (powerplatform.dataverse.models.labels.localizedlabel method)":[[17,"PowerPlatform.Dataverse.models.labels.LocalizedLabel.to_dict",false]],"to_dict() (powerplatform.dataverse.models.protocol.dataversemodel method)":[[18,"PowerPlatform.Dataverse.models.protocol.DataverseModel.to_dict",false]],"to_dict() (powerplatform.dataverse.models.record.record method)":[[20,"PowerPlatform.Dataverse.models.record.Record.to_dict",false]],"to_dict() (powerplatform.dataverse.models.relationship.cascadeconfiguration method)":[[21,"PowerPlatform.Dataverse.models.relationship.CascadeConfiguration.to_dict",false]],"to_dict() (powerplatform.dataverse.models.relationship.lookupattributemetadata method)":[[21,"PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata.to_dict",false]],"to_dict() (powerplatform.dataverse.models.relationship.manytomanyrelationshipmetadata method)":[[21,"PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata.to_dict",false]],"to_dict() (powerplatform.dataverse.models.relationship.onetomanyrelationshipmetadata method)":[[21,"PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata.to_dict",false]],"to_dict() (powerplatform.dataverse.models.table_info.tableinfo method)":[[22,"PowerPlatform.Dataverse.models.table_info.TableInfo.to_dict",false]],"to_odata() (powerplatform.dataverse.models.filters.filterexpression method)":[[15,"PowerPlatform.Dataverse.models.filters.FilterExpression.to_odata",false]],"to_odata() (powerplatform.dataverse.models.query_builder.expandoption method)":[[19,"PowerPlatform.Dataverse.models.query_builder.ExpandOption.to_odata",false]],"top (powerplatform.dataverse.models.query_builder.queryparams attribute)":[[19,"PowerPlatform.Dataverse.models.query_builder.QueryParams.top",false]],"top() (powerplatform.dataverse.models.query_builder.expandoption method)":[[19,"PowerPlatform.Dataverse.models.query_builder.ExpandOption.top",false]],"type (powerplatform.dataverse.models.table_info.columninfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.ColumnInfo.type",false]],"unshare (powerplatform.dataverse.models.relationship.cascadeconfiguration attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.CascadeConfiguration.unshare",false]],"update() (powerplatform.dataverse.operations.batch.batchdataframeoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchDataFrameOperations.update",false]],"update() (powerplatform.dataverse.operations.batch.batchrecordoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchRecordOperations.update",false]],"update() (powerplatform.dataverse.operations.batch.changesetrecordoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.ChangeSetRecordOperations.update",false]],"update() (powerplatform.dataverse.operations.dataframe.dataframeoperations method)":[[25,"PowerPlatform.Dataverse.operations.dataframe.DataFrameOperations.update",false]],"update() (powerplatform.dataverse.operations.records.recordoperations method)":[[29,"PowerPlatform.Dataverse.operations.records.RecordOperations.update",false]],"upload() (powerplatform.dataverse.operations.files.fileoperations method)":[[26,"PowerPlatform.Dataverse.operations.files.FileOperations.upload",false]],"upsert() (powerplatform.dataverse.operations.batch.batchrecordoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchRecordOperations.upsert",false]],"upsert() (powerplatform.dataverse.operations.records.recordoperations method)":[[29,"PowerPlatform.Dataverse.operations.records.RecordOperations.upsert",false]],"upsertitem (class in powerplatform.dataverse.models.upsert)":[[23,"PowerPlatform.Dataverse.models.upsert.UpsertItem",false]],"user_agent_context (powerplatform.dataverse.core.config.operationcontext attribute)":[[4,"PowerPlatform.Dataverse.core.config.OperationContext.user_agent_context",false]],"user_localized_label (powerplatform.dataverse.models.labels.label attribute)":[[17,"PowerPlatform.Dataverse.models.labels.Label.user_localized_label",false]],"validationerror":[[5,"PowerPlatform.Dataverse.core.errors.ValidationError",false]],"values() (powerplatform.dataverse.models.record.record method)":[[20,"PowerPlatform.Dataverse.models.record.Record.values",false]],"values() (powerplatform.dataverse.models.table_info.tableinfo method)":[[22,"PowerPlatform.Dataverse.models.table_info.TableInfo.values",false]]},"objects":{"":[[32,0,0,"-","PowerPlatform"]],"PowerPlatform":[[10,0,0,"-","Dataverse"]],"PowerPlatform.Dataverse":[[0,0,0,"-","claude_skill"],[1,0,0,"-","client"],[3,0,0,"-","common"],[6,0,0,"-","core"],[8,0,0,"-","data"],[9,0,0,"-","extensions"],[11,0,0,"-","migration"],[16,0,0,"-","models"],[27,0,0,"-","operations"],[31,0,0,"-","utils"]],"PowerPlatform.Dataverse.client":[[1,1,1,"","DataverseClient"]],"PowerPlatform.Dataverse.client.DataverseClient":[[1,2,1,"","auth"],[1,2,1,"","batch"],[1,3,1,"","close"],[1,2,1,"","dataframe"],[1,2,1,"","files"],[1,3,1,"","flush_cache"],[1,2,1,"","query"],[1,2,1,"","records"],[1,2,1,"","tables"]],"PowerPlatform.Dataverse.common":[[2,0,0,"-","constants"]],"PowerPlatform.Dataverse.common.constants":[[2,4,1,"","CASCADE_BEHAVIOR_CASCADE"],[2,4,1,"","CASCADE_BEHAVIOR_NO_CASCADE"],[2,4,1,"","CASCADE_BEHAVIOR_REMOVE_LINK"],[2,4,1,"","CASCADE_BEHAVIOR_RESTRICT"],[2,4,1,"","ODATA_TYPE_LABEL"],[2,4,1,"","ODATA_TYPE_LOCALIZED_LABEL"],[2,4,1,"","ODATA_TYPE_LOOKUP_ATTRIBUTE"],[2,4,1,"","ODATA_TYPE_MANY_TO_MANY_RELATIONSHIP"],[2,4,1,"","ODATA_TYPE_ONE_TO_MANY_RELATIONSHIP"]],"PowerPlatform.Dataverse.core":[[4,0,0,"-","config"],[5,0,0,"-","errors"],[7,0,0,"-","log_config"]],"PowerPlatform.Dataverse.core.config":[[4,1,1,"","DataverseConfig"],[4,1,1,"","OperationContext"]],"PowerPlatform.Dataverse.core.config.DataverseConfig":[[4,3,1,"","from_env"],[4,2,1,"","http_backoff"],[4,2,1,"","http_retries"],[4,2,1,"","http_timeout"],[4,2,1,"","language_code"],[4,2,1,"","log_config"],[4,2,1,"","operation_context"]],"PowerPlatform.Dataverse.core.config.OperationContext":[[4,2,1,"","user_agent_context"]],"PowerPlatform.Dataverse.core.errors":[[5,5,1,"","DataverseError"],[5,5,1,"","HttpError"],[5,5,1,"","MetadataError"],[5,5,1,"","SQLParseError"],[5,5,1,"","ValidationError"]],"PowerPlatform.Dataverse.core.errors.DataverseError":[[5,2,1,"","code"],[5,2,1,"","details"],[5,2,1,"","is_transient"],[5,2,1,"","message"],[5,2,1,"","source"],[5,2,1,"","status_code"],[5,2,1,"","subcode"],[5,2,1,"","timestamp"],[5,3,1,"","to_dict"]],"PowerPlatform.Dataverse.core.log_config":[[7,1,1,"","LogConfig"]],"PowerPlatform.Dataverse.core.log_config.LogConfig":[[7,2,1,"","backup_count"],[7,2,1,"","log_file_prefix"],[7,2,1,"","log_folder"],[7,2,1,"","log_level"],[7,2,1,"","max_body_bytes"],[7,2,1,"","max_file_bytes"],[7,2,1,"","redacted_headers"]],"PowerPlatform.Dataverse.migration":[[12,0,0,"-","migrate_v0_to_v1"]],"PowerPlatform.Dataverse.migration.migrate_v0_to_v1":[[12,6,1,"","find_manual_patterns"],[12,6,1,"","main"],[12,6,1,"","migrate_file"],[12,6,1,"","migrate_source"]],"PowerPlatform.Dataverse.models":[[13,0,0,"-","batch"],[14,0,0,"-","fetchxml_query"],[15,0,0,"-","filters"],[17,0,0,"-","labels"],[18,0,0,"-","protocol"],[19,0,0,"-","query_builder"],[20,0,0,"-","record"],[21,0,0,"-","relationship"],[22,0,0,"-","table_info"],[23,0,0,"-","upsert"]],"PowerPlatform.Dataverse.models.batch":[[13,1,1,"","BatchItemResponse"],[13,1,1,"","BatchResult"]],"PowerPlatform.Dataverse.models.batch.BatchItemResponse":[[13,2,1,"","content_id"],[13,2,1,"","data"],[13,2,1,"","entity_id"],[13,2,1,"","error_code"],[13,2,1,"","error_message"],[13,7,1,"","is_success"],[13,2,1,"","status_code"]],"PowerPlatform.Dataverse.models.batch.BatchResult":[[13,7,1,"","entity_ids"],[13,7,1,"","failed"],[13,7,1,"","has_errors"],[13,2,1,"","responses"],[13,7,1,"","succeeded"]],"PowerPlatform.Dataverse.models.fetchxml_query":[[14,1,1,"","FetchXmlQuery"]],"PowerPlatform.Dataverse.models.fetchxml_query.FetchXmlQuery":[[14,3,1,"","execute"],[14,3,1,"","execute_pages"]],"PowerPlatform.Dataverse.models.filters":[[15,1,1,"","ColumnProxy"],[15,1,1,"","FilterExpression"],[15,6,1,"","between"],[15,6,1,"","col"],[15,6,1,"","contains"],[15,6,1,"","endswith"],[15,6,1,"","eq"],[15,6,1,"","filter_in"],[15,6,1,"","ge"],[15,6,1,"","gt"],[15,6,1,"","is_not_null"],[15,6,1,"","is_null"],[15,6,1,"","le"],[15,6,1,"","lt"],[15,6,1,"","ne"],[15,6,1,"","not_between"],[15,6,1,"","not_in"],[15,6,1,"","raw"],[15,6,1,"","startswith"]],"PowerPlatform.Dataverse.models.filters.ColumnProxy":[[15,3,1,"","between"],[15,3,1,"","contains"],[15,3,1,"","endswith"],[15,3,1,"","in_"],[15,3,1,"","is_not_null"],[15,3,1,"","is_null"],[15,3,1,"","like"],[15,3,1,"","not_between"],[15,3,1,"","not_in"],[15,3,1,"","not_like"],[15,3,1,"","startswith"]],"PowerPlatform.Dataverse.models.filters.FilterExpression":[[15,3,1,"","to_odata"]],"PowerPlatform.Dataverse.models.labels":[[17,1,1,"","Label"],[17,1,1,"","LocalizedLabel"]],"PowerPlatform.Dataverse.models.labels.Label":[[17,2,1,"","additional_properties"],[17,2,1,"","localized_labels"],[17,3,1,"","to_dict"],[17,2,1,"","user_localized_label"]],"PowerPlatform.Dataverse.models.labels.LocalizedLabel":[[17,2,1,"","additional_properties"],[17,2,1,"","label"],[17,2,1,"","language_code"],[17,3,1,"","to_dict"]],"PowerPlatform.Dataverse.models.protocol":[[18,1,1,"","DataverseModel"]],"PowerPlatform.Dataverse.models.protocol.DataverseModel":[[18,3,1,"","from_dict"],[18,3,1,"","to_dict"]],"PowerPlatform.Dataverse.models.query_builder":[[19,1,1,"","ExpandOption"],[19,1,1,"","QueryBuilder"],[19,1,1,"","QueryParams"]],"PowerPlatform.Dataverse.models.query_builder.ExpandOption":[[19,3,1,"","filter"],[19,3,1,"","order_by"],[19,2,1,"","relation"],[19,3,1,"","select"],[19,3,1,"","to_odata"],[19,3,1,"","top"]],"PowerPlatform.Dataverse.models.query_builder.QueryBuilder":[[19,3,1,"","execute"],[19,3,1,"","execute_pages"],[19,3,1,"","to_dataframe"]],"PowerPlatform.Dataverse.models.query_builder.QueryParams":[[19,2,1,"","count"],[19,2,1,"","expand"],[19,2,1,"","filter"],[19,2,1,"","include_annotations"],[19,2,1,"","orderby"],[19,2,1,"","page_size"],[19,2,1,"","select"],[19,2,1,"","table"],[19,2,1,"","top"]],"PowerPlatform.Dataverse.models.record":[[20,1,1,"","QueryResult"],[20,1,1,"","Record"]],"PowerPlatform.Dataverse.models.record.QueryResult":[[20,3,1,"","first"],[20,2,1,"","records"],[20,3,1,"","to_dataframe"]],"PowerPlatform.Dataverse.models.record.Record":[[20,2,1,"","data"],[20,2,1,"","etag"],[20,3,1,"","from_api_response"],[20,3,1,"","get"],[20,2,1,"","id"],[20,3,1,"","items"],[20,3,1,"","keys"],[20,2,1,"","table"],[20,3,1,"","to_dict"],[20,3,1,"","values"]],"PowerPlatform.Dataverse.models.relationship":[[21,1,1,"","CascadeConfiguration"],[21,1,1,"","LookupAttributeMetadata"],[21,1,1,"","ManyToManyRelationshipMetadata"],[21,1,1,"","OneToManyRelationshipMetadata"],[21,1,1,"","RelationshipInfo"]],"PowerPlatform.Dataverse.models.relationship.CascadeConfiguration":[[21,2,1,"","additional_properties"],[21,2,1,"","assign"],[21,2,1,"","delete"],[21,2,1,"","merge"],[21,2,1,"","reparent"],[21,2,1,"","share"],[21,3,1,"","to_dict"],[21,2,1,"","unshare"]],"PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata":[[21,2,1,"","additional_properties"],[21,2,1,"","description"],[21,2,1,"","display_name"],[21,2,1,"","required_level"],[21,2,1,"","schema_name"],[21,3,1,"","to_dict"]],"PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata":[[21,2,1,"","additional_properties"],[21,2,1,"","entity1_logical_name"],[21,2,1,"","entity2_logical_name"],[21,2,1,"","intersect_entity_name"],[21,2,1,"","schema_name"],[21,3,1,"","to_dict"]],"PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata":[[21,2,1,"","additional_properties"],[21,2,1,"","cascade_configuration"],[21,2,1,"","referenced_attribute"],[21,2,1,"","referenced_entity"],[21,2,1,"","referencing_attribute"],[21,2,1,"","referencing_entity"],[21,2,1,"","schema_name"],[21,3,1,"","to_dict"]],"PowerPlatform.Dataverse.models.relationship.RelationshipInfo":[[21,2,1,"","entity1_logical_name"],[21,2,1,"","entity2_logical_name"],[21,3,1,"","from_api_response"],[21,3,1,"","from_many_to_many"],[21,3,1,"","from_one_to_many"],[21,2,1,"","lookup_schema_name"],[21,2,1,"","referenced_entity"],[21,2,1,"","referencing_entity"],[21,2,1,"","relationship_id"],[21,2,1,"","relationship_schema_name"],[21,2,1,"","relationship_type"]],"PowerPlatform.Dataverse.models.table_info":[[22,1,1,"","AlternateKeyInfo"],[22,1,1,"","ColumnInfo"],[22,1,1,"","TableInfo"]],"PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo":[[22,3,1,"","from_api_response"],[22,2,1,"","key_attributes"],[22,2,1,"","metadata_id"],[22,2,1,"","schema_name"],[22,2,1,"","status"]],"PowerPlatform.Dataverse.models.table_info.ColumnInfo":[[22,2,1,"","description"],[22,2,1,"","display_name"],[22,3,1,"","from_api_response"],[22,2,1,"","is_primary"],[22,2,1,"","is_required"],[22,2,1,"","logical_name"],[22,2,1,"","max_length"],[22,2,1,"","schema_name"],[22,2,1,"","type"]],"PowerPlatform.Dataverse.models.table_info.TableInfo":[[22,2,1,"","columns"],[22,2,1,"","columns_created"],[22,2,1,"","description"],[22,2,1,"","display_name"],[22,2,1,"","entity_set_name"],[22,3,1,"","from_api_response"],[22,3,1,"","from_dict"],[22,3,1,"","get"],[22,3,1,"","items"],[22,3,1,"","keys"],[22,2,1,"","logical_name"],[22,2,1,"","metadata_id"],[22,2,1,"","primary_id_attribute"],[22,2,1,"","primary_name_attribute"],[22,2,1,"","schema_name"],[22,3,1,"","to_dict"],[22,3,1,"","values"]],"PowerPlatform.Dataverse.models.upsert":[[23,1,1,"","UpsertItem"]],"PowerPlatform.Dataverse.models.upsert.UpsertItem":[[23,2,1,"","alternate_key"],[23,2,1,"","record"]],"PowerPlatform.Dataverse.operations":[[24,0,0,"-","batch"],[25,0,0,"-","dataframe"],[26,0,0,"-","files"],[28,0,0,"-","query"],[29,0,0,"-","records"],[30,0,0,"-","tables"]],"PowerPlatform.Dataverse.operations.batch":[[24,1,1,"","BatchDataFrameOperations"],[24,1,1,"","BatchOperations"],[24,1,1,"","BatchQueryOperations"],[24,1,1,"","BatchRecordOperations"],[24,1,1,"","BatchRequest"],[24,1,1,"","BatchTableOperations"],[24,1,1,"","ChangeSet"],[24,1,1,"","ChangeSetRecordOperations"]],"PowerPlatform.Dataverse.operations.batch.BatchDataFrameOperations":[[24,3,1,"","create"],[24,3,1,"","delete"],[24,3,1,"","update"]],"PowerPlatform.Dataverse.operations.batch.BatchOperations":[[24,3,1,"","new"]],"PowerPlatform.Dataverse.operations.batch.BatchQueryOperations":[[24,3,1,"","sql"]],"PowerPlatform.Dataverse.operations.batch.BatchRecordOperations":[[24,3,1,"","create"],[24,3,1,"","delete"],[24,3,1,"","get"],[24,3,1,"","list"],[24,3,1,"","retrieve"],[24,3,1,"","update"],[24,3,1,"","upsert"]],"PowerPlatform.Dataverse.operations.batch.BatchRequest":[[24,3,1,"","changeset"],[24,2,1,"","dataframe"],[24,3,1,"","execute"],[24,2,1,"","query"],[24,2,1,"","records"],[24,2,1,"","tables"]],"PowerPlatform.Dataverse.operations.batch.BatchTableOperations":[[24,3,1,"","add_columns"],[24,3,1,"","create"],[24,3,1,"","create_lookup_field"],[24,3,1,"","create_many_to_many_relationship"],[24,3,1,"","create_one_to_many_relationship"],[24,3,1,"","delete"],[24,3,1,"","delete_relationship"],[24,3,1,"","get"],[24,3,1,"","get_relationship"],[24,3,1,"","list"],[24,3,1,"","remove_columns"]],"PowerPlatform.Dataverse.operations.batch.ChangeSet":[[24,2,1,"","records"]],"PowerPlatform.Dataverse.operations.batch.ChangeSetRecordOperations":[[24,3,1,"","create"],[24,3,1,"","delete"],[24,3,1,"","update"]],"PowerPlatform.Dataverse.operations.dataframe":[[25,1,1,"","DataFrameOperations"]],"PowerPlatform.Dataverse.operations.dataframe.DataFrameOperations":[[25,3,1,"","create"],[25,3,1,"","delete"],[25,3,1,"","get"],[25,3,1,"","sql"],[25,3,1,"","update"]],"PowerPlatform.Dataverse.operations.files":[[26,1,1,"","FileOperations"]],"PowerPlatform.Dataverse.operations.files.FileOperations":[[26,3,1,"","upload"]],"PowerPlatform.Dataverse.operations.query":[[28,1,1,"","QueryOperations"]],"PowerPlatform.Dataverse.operations.query.QueryOperations":[[28,3,1,"","builder"],[28,3,1,"","fetchxml"],[28,3,1,"","odata_bind"],[28,3,1,"","odata_expand"],[28,3,1,"","odata_expands"],[28,3,1,"","odata_select"],[28,3,1,"","sql"],[28,3,1,"","sql_columns"]],"PowerPlatform.Dataverse.operations.records":[[29,1,1,"","RecordOperations"]],"PowerPlatform.Dataverse.operations.records.RecordOperations":[[29,3,1,"","create"],[29,3,1,"","delete"],[29,3,1,"","get"],[29,3,1,"","list"],[29,3,1,"","list_pages"],[29,3,1,"","retrieve"],[29,3,1,"","update"],[29,3,1,"","upsert"]],"PowerPlatform.Dataverse.operations.tables":[[30,1,1,"","TableOperations"]],"PowerPlatform.Dataverse.operations.tables.TableOperations":[[30,3,1,"","add_columns"],[30,3,1,"","create"],[30,3,1,"","create_alternate_key"],[30,3,1,"","create_lookup_field"],[30,3,1,"","create_many_to_many_relationship"],[30,3,1,"","create_one_to_many_relationship"],[30,3,1,"","delete"],[30,3,1,"","delete_alternate_key"],[30,3,1,"","delete_relationship"],[30,3,1,"","get"],[30,3,1,"","get_alternate_keys"],[30,3,1,"","get_relationship"],[30,3,1,"","list"],[30,3,1,"","list_columns"],[30,3,1,"","list_relationships"],[30,3,1,"","list_table_relationships"],[30,3,1,"","remove_columns"]]},"objnames":{"0":["py","module","Python module"],"1":["py","class","Python class"],"2":["py","attribute","Python attribute"],"3":["py","method","Python method"],"4":["py","data","Python data"],"5":["py","exception","Python exception"],"6":["py","function","Python function"],"7":["py","property","Python property"]},"objtypes":{"0":"py:module","1":"py:class","2":"py:attribute","3":"py:method","4":"py:data","5":"py:exception","6":"py:function","7":"py:property"},"terms":{"0b":12,"100_000":28,"10_485_760":7,"123456789abc":30,"1_000_000":[15,19,28],"20s":28,"2xx":13,"30s":28,"4xx":[5,24],"5xx":5,"A":[15,24,28,29],"AND":[15,28],"After":[1,5,30],"All":[1,13,14,19,24,25,29],"An":24,"At":19,"BETWEEN":28,"BY":[25,28],"Between":15,"Both":13,"By":30,"Cannot":1,"Do":[2,21,24],"Each":[19,24,25,28,29],"FROM":[24,25,28],"For":[13,25,28,30],"HAVING":28,"IN":28,"IS":28,"If":[1,4,15,19,20,21,24,25,26,28,29,30],"In":15,"It":1,"NOT":[12,28],"No":[14,28],"Not":[15,28],"ON":[25,28],"OR":[15,28],"On":24,"Or":12,"The":[1,4,14,17,19,21,24,25,26,28,29,30],"Their":13,"These":[2,17,21],"This":[0,1,3,5,6,8,15,19,22,24,27,29,30,33],"WHERE":[24,25,28],"When":[4,7,24,25,26,28,29,30],"With":[19,28],"You":14,"Your":1,"__entity_logical_name__":18,"__entity_set_name__":18,"_base":28,"_batch":24,"_batchcontext":24,"_by_page_unset":19,"_changeset":24,"_create_t":22,"_get_table_info":22,"_name":30,"_querybuilderbas":19,"abc":29,"abstractmethod":15,"acc":[23,24,29],"accept":24,"access":[8,13,20,22,24,25,26,28,29,30],"accident":19,"account":[1,17,18,19,20,21,22,24,25,26,28,29,30],"account_id":[20,24,25,26,29],"account_task":19,"accountid":[21,24,25,28],"accountnumb":[23,24,29],"acct":28,"acct_id":28,"accur":[5,19],"acm":24,"across":[3,14],"action":[2,13,24,25,29],"activ":[7,22,25,30],"ada":24,"adapt":31,"add":[1,12,13,18,24,25,29,30],"add_column":[12,13,24,30],"addit":[5,17,21,24],"additional_properti":[17,21],"address":4,"address1_postalcod":[23,29],"agent":[1,4],"aggreg":[25,28],"alia":28,"alic":24,"align":25,"allow":[1,4,19,24,30],"allowlist":4,"along":30,"alongsid":[25,29],"alreadi":[12,24,30],"also":30,"altern":[22,23,24,29,30],"alternate_key":[23,24,29],"alternatekeyinfo":[22,30],"alway":[26,28],"ani":[1,2,5,12,13,15,17,18,20,21,22,23,24,28,29,30],"annot":[20,24,25,29],"anoth":28,"api":[1,2,5,17,20,21,22,25,28,30,34],"app":4,"appear":13,"append":[1,4,7],"appli":[2,29,30],"applic":[0,26],"applicationrequir":21,"archiv":21,"argument":[12,29],"argv":12,"around":[20,25],"arriv":[14,24],"asc":[24,28,29],"assert":18,"assign":21,"associ":[2,30],"async":[24,25,29],"atom":24,"attach":24,"attempt":[4,24],"attribut":[1,4,21,22,23,24,25,26,28,30],"attributemetadata":22,"attributetyp":[21,30],"attributetypenam":21,"auth":[1,24],"authent":[1,6],"author":7,"auto":[21,26,28,33],"autoapi":33,"autocomplet":19,"automat":[1,4,7,14,23,26,29],"avail":[19,20,24,28],"avg":28,"avoid":25,"azur":1,"b":29,"babbag":24,"back":[24,29],"backoff":4,"backup":7,"backup_count":7,"backward":[20,22,30],"base":[5,15,18,19,26],"base_url":[1,25,26,28,29,30],"basenam":26,"basic":[28,30],"batch":[1,12,14,16,25,27,33],"batchdataframeoper":24,"batchitemrespons":[13,24],"batchoper":24,"batchqueryoper":24,"batchrecordoper":24,"batchrequest":24,"batchresult":[13,24],"batchtableoper":24,"becaus":[15,24,28],"becom":[24,25],"befor":[5,7,14,19,24],"behavior":[21,30],"behaviour":24,"besid":25,"beta":12,"bind":[24,28],"block":14,"bob":24,"bodi":[5,7,13,24],"body_excerpt":5,"bool":[5,12,13,19,22,24,25,26,28,29,30],"boolean":30,"bound":[15,19,24,28],"break":[1,12],"broad":28,"broadcast":[24,25,29],"build":[15,19,28,30],"builder":[12,16,19,24,28],"builder_chain":12,"bulk":[24,29],"bulk_id":13,"bulkdelet":[24,25,29],"bundl":24,"busi":7,"by_pag":[12,19],"byte":7,"c":[25,28],"cach":1,"call":[1,5,12,14,19,24,25,28],"caller":[1,4,12,24],"can":[15,17,19,21,28],"canbechang":21,"capabl":[1,8],"captur":7,"cascad":[2,21,24,30],"cascade_behavior_cascad":2,"cascade_behavior_no_cascad":2,"cascade_behavior_remove_link":[2,24,30],"cascade_behavior_restrict":2,"cascade_configur":[21,30],"cascade_delet":[24,30],"cascadeconfigur":[21,30],"case":[7,15,19,24,25,28,29,30],"categori":5,"caus":30,"ch1":29,"ch2":29,"chain":[12,19,28],"chang":[12,20,24,25,29],"changeset":[13,24],"changesetrecordoper":24,"charact":4,"check":[15,24],"child":[21,24,30],"chunk":[1,26],"class":27,"classmethod":[4,18,20,21,22],"claud":0,"claude_skil":[10,33],"claus":30,"cleanup":1,"clear":[1,24,25],"clear_nul":[24,25],"client":[0,4,5,6,10,12,13,14,18,19,20,21,22,24,25,26,28,29,30,33],"client_request_id":5,"client_var":12,"close":[1,4],"closest":15,"cls":18,"cnt":[25,28],"coalesc":28,"code":[0,5,13,17,20,22,24,30],"codemod":12,"col":[12,15,16,19,28,29,30],"col1":19,"col2":19,"collect":[15,19,20,28,29],"column":[1,2,13,15,19,22,24,25,26,28,29,30],"columninfo":22,"columnproxi":15,"columns_cr":[22,30],"com":[1,25],"combin":[24,30],"comment":[4,12],"common":[10,28,30,33],"communic":1,"communiti":[24,25,29],"comparison":15,"compat":[20,22,30],"compil":[15,19],"complex":[15,19],"compon":6,"compos":[15,16,19,22,28,30],"composit":[15,29],"concret":12,"concurr":20,"condit":15,"config":[1,6,21,33],"configur":[1,4,6,7,21,29],"connect":1,"consid":25,"consolid":[19,25],"constant":[3,30,33],"constraint":19,"construct":[1,15,19,24,28],"constructor":4,"contact":[21,24,25,28,29],"contactid":[25,28],"contain":[0,3,4,5,6,7,8,12,13,15,19,24,25,27,28,29,33],"content_id":13,"context":[1,4,5,24],"continu":[20,24],"continue_on_error":24,"contoso":[1,15,23,24,25,29],"contract":26,"contribut":[0,13,24],"control":4,"conveni":[4,24,30],"convers":[1,24],"convert":[5,17,18,21,24,25],"copi":20,"core":[1,10,33],"correl":5,"correlation_id":5,"correspond":[22,24,29],"count":[19,24,25,28,29],"counterpart":29,"creat":[1,4,7,12,13,18,19,20,21,22,24,25,26,28,29,30,33],"create_alternate_key":30,"create_column":12,"create_lookup_field":[21,24,30],"create_many_to_many_relationship":[21,24,30],"create_one_to_many_relationship":[21,24,30],"create_t":12,"createdon":[15,19,29],"createmultipl":[13,24,25,29],"creation":[22,24,29,30],"credenti":[1,25,26,28,29,30],"crm":[1,2,15,17,21,30],"cross":28,"crud":[1,8,18,25,29],"cs":24,"cs_intern":24,"cte":28,"current":[1,9,26],"custom":[1,30],"custom_t":30,"d":12,"data":[7,10,13,16,18,20,22,23,24,29,33],"dataclass":[16,18],"datafram":[1,12,19,20,24,27,33],"dataframeoper":25,"datavers":[32,33],"dataverse_20260310_143022":7,"dataverse_log":7,"dataversecli":[1,7,14,24,25,26,28,29,30],"dataverseconfig":[1,4,7],"dataverseerror":5,"dataversemodel":[16,18],"date":[28,30],"datetim":30,"debug":7,"decim":[22,30],"def":18,"default":[1,4,7,17,19,20,21,22,24,25,26,28,29,30],"defin":[1,2,4,21,29,30],"definit":[16,22,30],"deleg":[1,25],"delet":[1,2,12,13,21,24,25,28,29,30],"delete_alternate_key":30,"delete_column":12,"delete_relationship":[24,30],"delete_t":12,"depart":30,"depend":29,"deprec":[12,15,19,24],"depth":28,"desc":[28,29],"descend":[19,28],"descript":[12,21,22,24,29,30],"detail":5,"detect":21,"determin":28,"dev":0,"develop":[0,12],"df":[14,19,24,25,28],"diagnost":[4,5,7],"dict":[5,13,17,18,20,21,22,23,24,28,29,30],"dictionari":[5,18,19,22,23,29,30],"differ":25,"direct":[18,21,24,28],"directori":7,"disabl":7,"discov":28,"discover":19,"dispatch":[18,24],"display":[21,22,24,25,28,29,30],"display_nam":[21,22,24,30],"displaynam":[21,30],"distinct":28,"distribut":5,"docstr":1,"document":[1,33],"doe":[4,25,26,28,29,30],"doesn":26,"domain":1,"doubl":30,"downstream":14,"dri":12,"dry_run":12,"dv":12,"dynam":[1,2,15,17,21,30],"e":[1,5,7,13,17,18,20,21,22,24,25,26,28,29,30],"eager":[19,28,29],"earli":14,"either":[5,21,29],"element":[14,24,28,29],"els":13,"email":[4,26],"email_id":26,"employe":30,"empti":[1,4,15,19,20,24,25,26,28,29,30],"enabl":[1,7,18],"end":28,"endpoint":28,"endswith":[12,15],"english":[4,17,30],"enqueu":24,"entiti":[13,14,16,18,19,20,21,22,24,28,29,30],"entity1_logical_nam":[21,30],"entity1logicalnam":21,"entity1navigationpropertynam":21,"entity2_logical_nam":[21,30],"entity2logicalnam":21,"entity_id":13,"entity_nam":14,"entity_set":24,"entity_set_nam":[22,30],"entitydefinit":[22,24,30],"entityid":13,"entitykeymetadata":22,"entityset":[1,28],"entitysetnam":30,"entri":[1,13,24,28,29],"enum":[24,30],"environ":[1,30],"eq":[15,19,24,25,29,30],"equal":[15,29],"equival":[12,15,24],"err":13,"error":[4,6,13,24,33],"error_cod":13,"error_messag":13,"escap":[15,23],"etag":20,"etc":[7,21,24],"everi":[7,14,29,30],"exact":[24,25,28,29,30],"exampl":[1,13,14,15,17,18,19,20,21,22,23,24,25,26,28,29,30],"exceed":24,"except":1,"excerpt":5,"exclud":[20,28],"execut":[1,12,13,14,19,20,24,25,28,29],"execute_pag":[12,14,19,28],"exist":[2,20,21,24,25,26,29,30],"exit":14,"expand":[13,19,24,25,28,29],"expandopt":19,"expans":19,"expect":29,"explicit":[20,25,26,28],"expr":[12,15],"expr1":15,"expr2":15,"express":[12,15,16,19,24,28,29,30],"extens":[10,33],"extract":[13,20],"f":[1,13,25,28,29,30],"fabrikam":[24,25,29],"fail":[12,13,22,24,26,30],"failur":[5,24],"fals":[5,12,19,22,24,25,26,28,29,30],"feed":24,"fetch":[14,25,28,29],"fetchxml":[14,16,28],"fetchxml_queri":[16,28,33],"fetchxmlqueri":[14,16,28],"field":[20,21,24,25,28,29,30],"file":[1,4,7,8,12,27,30,33],"file_column":26,"filenam":[7,26],"filenotfounderror":26,"fileoper":26,"filesystem":26,"filter":[12,16,19,24,25,28,29,30,33],"filter_":12,"filter_between":12,"filter_contain":12,"filter_endswith":12,"filter_eq":12,"filter_g":12,"filter_gt":12,"filter_in":[12,15],"filter_l":12,"filter_lt":12,"filter_n":12,"filter_not_between":12,"filter_not_in":12,"filter_not_nul":12,"filter_nul":12,"filter_raw":12,"filter_startswith":12,"filter_str":[15,19],"filterexpress":[15,24,29],"final":1,"find":14,"find_manual_pattern":12,"fire":14,"first":[1,20,21,24],"firstnam":[24,28],"flag":[12,19,28],"flat":[12,19,20],"float":[4,30],"fluent":[15,16,19,28],"fluentli":28,"flush":1,"flush_cach":1,"folder":7,"follow":[19,29],"form":[4,14,24,28],"format":[4,17,21,22],"formattedvalu":[24,25,29],"found":[23,24,25,28,29,30],"foundat":6,"free":4,"from_api_respons":[20,21,22],"from_dict":[18,22],"from_env":[1,4],"from_many_to_mani":21,"from_one_to_mani":21,"from_tabl":28,"frozenset":7,"full":[19,28],"fullnam":[24,28,29],"function":[8,28],"futur":[1,4,18,19,24,31],"g":[1,5,7,13,17,18,20,21,22,24,25,26,28,29,30],"ga":[12,15,24,29],"ge":15,"generat":[5,21,29,33],"get":[1,12,13,18,19,20,22,24,25,28,29,30],"get_alternate_key":30,"get_relationship":[21,24,30],"get_table_info":12,"given":30,"got":25,"greater":15,"group":[24,25,27,28],"gt":15,"guard":12,"guid":[13,20,21,22,23,24,25,26,28,29,30],"guidanc":0,"hand":18,"handl":[1,6,8,12,22],"has_error":13,"hatch":15,"head":25,"header":[1,4,5,7,13,24,25,26,29],"help":[5,19],"helper":[16,18],"hi":[12,15],"hierarchi":5,"high":[1,15],"hint":[24,29],"hold":14,"http":[1,4,5,6,7,13,14,19,24,28,29],"http_backoff":4,"http_error":5,"http_retri":4,"http_timeout":4,"httperror":[1,5,24,26,30],"https":[1,25],"human":[5,22,24,30],"id":[4,5,12,13,20,24,25,29,30],"id1":[24,29],"id2":[24,29],"id3":29,"id_column":[24,25],"ide":19,"ident":1,"identifi":[2,5,23,25,29,30],"idiom":15,"ids_seri":24,"if_none_match":26,"ignor":24,"immedi":[1,24],"immut":4,"implement":18,"import":[1,12,15,18,19,24,25,28,29,30],"importerror":20,"importsequencenumb":28,"in_":[12,15],"inact":30,"inc":[24,29],"includ":[6,7,13,17,21,23,24,25,28,29,30],"include_annot":[19,24,25,29],"include_system":28,"index":[22,25,30],"individu":[1,13,24,29],"inert":[14,28],"info":[22,30],"inform":5,"infrastructur":6,"inherit":21,"initi":[1,5,19],"inject":5,"inner":28,"inprogress":22,"input":[21,24,25],"insensit":[7,15],"insert":[12,28],"insid":24,"inspect":[1,30],"instal":[12,20],"instanc":[4,15,17,18,19,24,25,26,28,29,30],"instanti":24,"instead":[1,15,19,24,28,29,30],"int":[1,4,5,7,12,13,17,19,22,24,25,29,30],"integ":[1,22,23,30],"integr":18,"intend":4,"intent":[12,25],"intenum":30,"interact":1,"interactivebrowsercredenti":1,"interfac":[1,19],"intern":[1,22,24,25],"intersect":[21,30],"intersect_entity_nam":21,"intersectentitynam":21,"invalid":[24,25],"irrevers":30,"is_not_nul":[12,15],"is_nul":[12,15],"is_pk":28,"is_primari":22,"is_requir":22,"is_success":13,"is_transi":5,"iscustomiz":21,"isinst":18,"ispriv":[24,30],"issecur":21,"isvalidforadvancedfind":21,"item":[13,16,20,22,23,24,29],"itemstatus":30,"itemsview":20,"iter":[12,14,16,19,20,25,29],"jane":28,"job":[24,25,29],"job_id":29,"jobid":24,"join":[25,28],"json":[13,17,20,21,24],"just":28,"keep":[7,21],"key":[1,4,20,21,22,23,24,28,29,30],"key_attribut":[22,30],"key_id":30,"key_nam":30,"keysview":[20,22],"keyword":15,"kind":1,"known":20,"kw":12,"label":[1,2,4,16,21,23,24,28,30,33],"languag":[1,17,24,30],"language_cod":[4,17,24,30],"languagecod":17,"larg":[14,25],"large_fil":26,"last":[17,21],"lastnam":28,"layer":[8,22],"lazi":[19,28],"lazili":[1,14,19,29],"lcid":[4,17],"le":15,"lead":24,"lead_ref":24,"least":19,"left":[25,28],"legaci":[22,30],"legacy_key":22,"len":[13,25,29],"length":[22,29],"less":15,"let":24,"level":[1,7,12,21,24,25,29,30],"libcst":12,"lifecycl":1,"lightweight":[1,4],"like":[15,19,20,21,22,28,30],"limit":[19,24,25,28],"link":[21,28],"list":[12,13,17,19,20,22,24,25,28,29,30],"list_column":30,"list_pag":29,"list_relationship":30,"list_tabl":12,"list_table_relationship":30,"liter":12,"lo":[12,15],"load":1,"local":[4,7,17,26,30],"localized_label":[17,30],"localizedlabel":[2,17,21,30],"log":[4,7],"log_config":[4,6,33],"log_file_prefix":7,"log_fold":7,"log_level":7,"logconfig":[4,7],"logic":[15,18,21,22,24,25,27,28,29,30],"logical_nam":[22,30],"logicalnam":[21,30],"lookup":[21,24,28,30],"lookup_attribut":28,"lookup_field_nam":[24,30],"lookup_schema_nam":[21,30],"lookupattributemetadata":[2,21,24,30],"lookuptyp":21,"low":15,"lowercas":[15,22,23,25,28,29],"lt":15,"ltd":[1,23,24,29],"m":[12,28],"made":[14,28],"main":12,"make":25,"manag":[1,8,12,24,30],"mani":[21,24,30],"manual":[1,12,18,19,24,28],"manual_review_not":12,"many_to_mani":21,"manytomanyrelationship":30,"manytomanyrelationshipmetadata":[2,21,24,30],"manytoonerelationship":30,"map":[21,23,24,29,30],"mark":1,"match":[4,14,15,19,23,24,25,26,28,29],"math":28,"max":[7,28],"max_body_byt":7,"max_file_byt":7,"max_length":22,"maximum":[4,7,19,22,24,25,29],"maxpages":[24,29],"may":[1,5,7,14,29,30],"mb":7,"mechan":12,"medium":14,"memo":30,"memori":14,"merg":[17,21,28],"messag":[4,5,13],"metadata":[1,2,5,8,17,20,21,22,24,28,29,30],"metadata_id":[22,30],"metadataerror":[5,24,30],"metadataid":24,"method":[1,12,15,18,19,24,25,28,29,30],"microsoft":[1,2,15,17,21,30],"migrat":[10,33],"migrate_fil":12,"migrate_sourc":12,"migrate_v0_to_v1":[11,33],"mime":26,"mime_typ":26,"min":28,"mirror":[15,24],"miss":[1,7,24,25,28],"mode":[20,24,26],"model":[10,12,24,28,29,30,33],"modul":[3,6,8,27,31],"money":30,"ms":5,"multi":[24,28,29],"multilin":30,"multipl":[1,13,17,24,25,29],"multipli":4,"must":[19,24,25,29,30],"mysolut":30,"n":[28,30],"name":[1,7,12,14,15,18,19,20,21,22,23,24,25,26,28,29,30],"namespac":[1,12,24,25,26,27,28,29,30],"nan":[24,25],"narrow":[24,30],"nav":[19,28],"nav_properti":28,"navig":[19,24,25,28,29],"navprop":28,"ne":15,"need":[14,28],"negat":15,"neither":[24,29],"nest":[19,28],"network":[1,19,29],"never":25,"new":[13,22,24,29,30],"new_":30,"new_account_contact":21,"new_account_ord":21,"new_accountid":[21,30],"new_act":30,"new_attach":26,"new_contract":26,"new_depart":30,"new_department_employe":30,"new_departmentid":30,"new_df":25,"new_docu":26,"new_employe":30,"new_employee_project":30,"new_instock":30,"new_mytestt":[25,26,29,30],"new_not":30,"new_ord":[21,30],"new_pric":[22,30],"new_product":[22,24,30],"new_product_code_key":30,"new_productcod":30,"new_productnam":30,"new_project":30,"new_rat":30,"new_status":30,"new_testcolumn":30,"new_titl":30,"newli":30,"next":28,"nextlink":[19,24,29],"nocascad":[2,21],"non":[1,13,15,23,24,25,29,30],"none":[1,4,5,12,13,17,18,20,21,22,24,25,26,29,30],"not_between":[12,15],"not_in":[12,15],"not_lik":15,"notin":15,"now":18,"null":[15,24,25,26,28],"number":[1,4,5,7,19,24,25,29],"object":[1,4,14,16,20,28,29,30],"obtain":[14,24],"octet":26,"odata":[1,2,8,13,15,16,17,18,19,20,21,22,23,24,25,28,29,30],"odata_bind":28,"odata_expand":28,"odata_select":28,"odata_type_label":2,"odata_type_localized_label":2,"odata_type_lookup_attribut":2,"odata_type_many_to_many_relationship":2,"odata_type_one_to_many_relationship":2,"offset":28,"ok":13,"omit":[24,29,30],"onc":[14,19,29,30],"one":[13,14,19,21,23,24,25,28,29,30],"one_to_mani":[21,30],"onetomanyrelationship":30,"onetomanyrelationshipmetadata":[2,21,24,30],"onli":[1,4,7,19,21,24,25,26,28,29,30],"oper":[1,2,4,5,8,10,13,15,16,18,21,23,33],"operation_context":4,"operationcontext":[1,4],"opt":[7,19],"optimist":20,"option":[1,4,5,9,17,19,21,23,24,25,29,30],"order":[13,24,25,28],"order_bi":[19,28],"orderbi":[19,24,25,29],"org":1,"organiz":[1,27],"orient":[24,25],"origin":5,"originatingleadid":24,"otherwis":[19,29],"outbound":[1,4,5],"outgo":28,"output":21,"overload":15,"overrid":[17,21],"overriddencreatedon":28,"overwrit":26,"packag":0,"page":[12,14,19,20,24,25,28,29,33],"page_s":[19,24,25,28,29],"pagin":[1,20,24,25,29],"pair":[4,20,22,24,29],"panda":[1,19,20,24,25],"param":19,"paramet":[1,4,5,7,13,14,15,17,19,20,21,22,23,24,25,26,28,29,30],"parent":[14,21,24,25,26,28,29,30],"parentaccountid":15,"parentcustomerid":[25,28],"parentcustomerid_account":28,"parenthes":[4,28],"pars":[5,12,13],"part":13,"partner":29,"pascalcas":[21,22,28,30],"pass":[1,15,18,19,20,25,28,29,30],"patch":[13,24,25,29],"path":[7,12,26],"pathlib":12,"pattern":[12,15,20,24,25,29],"payload":[2,17,18,21,23,28],"pd":[24,25],"pdf":26,"pend":[22,30],"per":[7,13,14,19,24,25,29],"perform":[2,21,24,29],"permit":24,"picklist":[1,23],"pii":[4,7],"pip":12,"pk":28,"place":12,"placehold":[9,31],"plain":[18,19,20,24,29],"plugin":[1,4],"plus":[4,28],"point":28,"pool":1,"posit":[13,29],"post":[13,24],"potenti":5,"powerplatform":33,"pre":24,"prefer":[14,15,24,25,29],"prefix":[7,30],"present":[20,22],"preserv":[12,20],"prevent":[2,19,21,24],"preview":12,"primari":[21,22,23,24,28,29,30],"primary_column":[24,30],"primary_id_attribut":22,"primary_name_attribut":22,"primarycontactid":[24,29],"primaryid":1,"print":[1,13,15,19,20,21,22,24,25,28,29,30],"prior":24,"privat":30,"process":[14,19,28,29],"process_batch":19,"produc":[15,24,25],"product":30,"progress":14,"project":[24,30],"properti":[5,13,17,19,21,24,25,28,29,30],"propertynam":15,"propertyvalu":15,"protocol":[1,8,16,33],"provid":[1,4,5,7,15,16,19,21,24,25,26,28,29,30],"proxi":[7,15],"public":13,"pydant":18,"python":[1,7,12,15],"queri":[1,5,8,12,14,16,19,20,24,25,27,29,30,33],"query_build":[16,28,33],"query_sql":12,"querybuild":[12,15,16,19,28],"queryoper":[14,28],"queryparam":19,"queryresult":[12,14,16,19,20,29],"queu":30,"quot":23,"r":[20,28],"rais":[1,4,5,12,15,19,20,21,24,25,26,28,29,30],"rather":23,"raw":[12,15,16,19,20,21,22,30],"read":[1,28],"readabl":[5,22,24,30],"readi":[24,30],"rec":12,"receiv":[12,24],"recogn":21,"recognis":12,"recommend":[1,19,21,28],"reconstruct":[12,18],"record":[1,2,12,13,14,16,18,19,21,23,24,25,26,27,28,30,33],"record_id":[1,20,24,25,26,29],"recordoper":29,"redact":[4,7],"redacted_head":7,"reduc":15,"ref":24,"refer":[21,24,34],"referenc":[2,21,30],"referenced_attribut":[21,30],"referenced_ent":[21,30],"referenced_t":[24,30],"referencedattribut":21,"referencedent":[21,30],"referencing_attribut":21,"referencing_ent":[21,30],"referencing_t":[24,30],"referencingent":[21,30],"reject":4,"rel":[21,30],"relat":[19,21],"relationship":[16,24,28,30,33],"relationship_id":[21,24,30],"relationship_schema_nam":[21,30],"relationship_typ":[21,30],"relationshipinfo":[21,30],"relationshiptyp":30,"releas":[1,18,19,24],"reli":24,"remain":14,"remov":[1,2,12,19,21,24,30],"remove_column":[12,24,30],"removelink":[2,21,30],"renam":12,"repar":21,"replac":[7,12,15,24,29],"report":14,"repres":[17,23,24,25],"represent":5,"request":[1,4,5,7,13,14,19,20,24,25,28,29,30],"requir":[4,12,18,21,22,24,30],"required_level":21,"requiredlevel":21,"reserv":4,"resolut":24,"resolv":[23,24],"resourc":1,"resp":13,"respons":[1,4,5,7,13,18,20,21,22,24,25,29,30],"response_data":[20,21,22],"restrict":[2,21,24,30],"result":[12,13,14,16,19,20,21,24,25,28,29,30],"retri":[1,4,5],"retriev":[12,22,24,25,29,30],"retry_aft":5,"return":[1,4,5,12,13,14,15,18,19,20,21,22,24,25,28,29,30],"revenu":[15,19,25,28],"review":12,"rewrit":12,"rewritten":12,"right":28,"roll":[18,24],"rollupview":21,"root":28,"rotat":7,"round":[14,24],"row":[14,24,25,28],"rule":15,"run":12,"runtime_check":18,"runtimeerror":[1,19],"s":[1,13,24,29],"safe":[1,19],"save":18,"scan":19,"scenario":28,"schema":[14,19,20,21,22,24,25,26,28,29,30],"schema_nam":[21,22,24,30],"schemanam":[21,30],"scienc":24,"script":12,"sdk":[0,3,5,6,7,8,9,12,15,16,22,23,24,25,26,27,28,29,30,31],"search":[1,28],"second":[4,5,21],"section":12,"securitytyp":21,"see":[5,12,19],"select":[12,19,20,24,25,26,28,29,30],"self":[5,18,19],"semicolon":4,"send":[24,26],"sensit":[4,7,19,24,25,29],"sent":[24,25,29],"separ":4,"sequenti":[24,25,29],"seri":[24,25],"server":[5,24,25,29],"servic":[5,13],"service_error_cod":5,"service_request_id":5,"session":[1,7],"set":[4,13,14,18,19,21,22,23,24,25,26,28,30],"share":[3,21],"shortcut":12,"shot":[14,19,29],"side":[5,29],"signatur":[1,5,19,24],"silent":[12,25],"simpl":[1,14,30],"simplifi":28,"sinc":[15,19,24],"singl":[1,13,15,19,20,23,24,25,28,29,30],"site":12,"size":[7,24,25,26,29],"skill":[0,4],"skip":[14,24,25],"slash":1,"small":[14,26],"smaller":25,"snake_cas":[21,22],"solut":[24,30],"sort":[19,24,25,29],"sourc":[5,12,28],"special":5,"specif":[1,5,17,19,24,25,30],"specifi":[7,18,19,21,24,25,26,28,30],"sphinx":33,"split":25,"sql":[1,5,8,12,20,24,25,28],"sql_column":28,"sql_join":12,"sql_select":12,"sqlparseerror":5,"stabl":1,"standalon":19,"standard":24,"start":30,"startswith":[12,15,30],"state":[1,4],"statecod":[15,19,24,25,28,29],"statement":[24,25,28],"status":[5,13,22,29,30],"status_cod":[5,13],"statuscod":[24,29],"stop":[14,24],"store":26,"str":[1,4,5,7,12,13,14,15,17,18,19,20,21,22,23,24,25,26,28,29,30],"strategi":26,"stream":[14,26,29],"string":[4,14,15,19,20,22,23,24,25,28,29,30],"strip":[14,20],"strong":20,"structur":[5,18,19,20],"style":15,"sub":30,"subcategori":5,"subclass":[24,30],"subcod":5,"subject":19,"submiss":[13,24],"submit":[13,24],"subqueri":28,"subsequ":24,"subset":[24,28],"succeed":[5,13,24,26],"success":[1,13],"suffix":28,"suitabl":28,"sum":28,"suppli":[24,30],"support":[1,15,18,21,22,24,25,28,29,30],"svc":12,"sync":19,"syntax":[12,19],"system":28,"t":[12,26,28],"tabl":[1,2,12,18,19,20,21,22,24,25,26,27,28,29,33],"table_info":[16,30,33],"table_logical_nam":30,"table_schema_nam":[22,30],"tableinfo":[22,30],"tableoper":30,"target":[21,23,24,26,28],"target_entity_set":28,"target_id":28,"target_t":28,"task":19,"telephone1":[1,18,19,23,24,25,29],"text":[4,17,30],"three":[13,29],"time":[1,24,28,29],"timeout":[1,4,25],"timestamp":[4,5,7],"timezoneruleversionnumb":28,"to_datafram":[12,14,19,20,28,29],"to_dict":[5,17,18,20,21,22],"to_odata":[15,19],"to_tabl":28,"today":[15,18],"togeth":[1,24],"tokencredenti":1,"tool":[1,4],"top":[12,19,24,25,28,29],"total":[25,29],"trace":[5,7],"tracepar":5,"track":5,"trail":1,"transact":24,"transient":[4,5],"transit":30,"transpar":24,"tree":[12,15,19,28],"tri":1,"trigger":[19,29],"trim":1,"trip":[14,24],"true":[12,13,19,21,24,25,26,28,29,30],"tune":4,"tupl":[12,22],"two":[0,21,29],"type":[1,2,4,5,12,13,14,15,16,17,18,19,20,21,22,24,25,26,28,29,30],"typeddict":19,"typeerror":[24,25,29],"typic":[5,21,30],"unchang":[15,25],"understand":12,"union":28,"uniqu":[24,30],"unit":4,"unknown":4,"unrecogn":21,"unshar":21,"unsupport":24,"updat":[1,12,13,18,24,25,28,29],"updatemultipl":[24,25],"upfront":14,"upload":[1,8,12,26],"upload_fil":12,"upsert":[16,24,29,30,33],"upsertitem":[16,23,24,29],"upsertmultipl":[13,24,29],"uri":24,"url":[1,23,29],"usabl":[24,28],"usag":[12,29],"use":[0,1,2,3,4,12,14,15,19,20,21,23,24,25,26,28,29,30],"use_bulk_delet":[24,25,29],"user":[1,4,17],"user_agent_context":4,"user_localized_label":17,"userlocalizedlabel":17,"usual":21,"utcconversiontimezonecod":28,"util":[3,10,33],"v":12,"v0":12,"v1":[12,24,25,29],"val":12,"valid":[4,5,21],"validation_error":5,"validationerror":[5,24,25,28],"valu":[1,2,4,5,7,13,15,17,20,21,22,23,24,25,28,29,30],"valueerror":[1,4,15,19,21,24,25,28,29],"valuesview":20,"var":12,"variabl":[12,18],"verbatim":15,"veri":25,"version":[15,17,19,24],"versionnumb":28,"via":[1,7,12,13,14,16,19,24,25,26,28,29,30],"virtual":28,"vs":21,"w3c":5,"wait":5,"want":[14,25],"warn":15,"was_chang":12,"web":[1,2,5,17,21,22,28,30],"websiteurl":25,"well":[14,28,30],"whether":[5,22,24,30],"whitespac":[12,25],"whose":[7,30],"will":[1,12,15,18,19,24,26,30],"window":28,"within":[5,13,24],"without":[1,12,18,20,23,24,25],"work":[18,20],"wrap":[20,30],"wrapper":[1,16,20,24,25],"write":[7,12,14,24,28],"written":22,"x":5,"xml":[14,28],"yet":18,"yield":[14,19,29],"zip":26},"titles":["PowerPlatform.Dataverse.claude_skill","PowerPlatform.Dataverse.client","PowerPlatform.Dataverse.common.constants","PowerPlatform.Dataverse.common","PowerPlatform.Dataverse.core.config","PowerPlatform.Dataverse.core.errors","PowerPlatform.Dataverse.core","PowerPlatform.Dataverse.core.log_config","PowerPlatform.Dataverse.data","PowerPlatform.Dataverse.extensions","PowerPlatform.Dataverse","PowerPlatform.Dataverse.migration","PowerPlatform.Dataverse.migration.migrate_v0_to_v1","PowerPlatform.Dataverse.models.batch","PowerPlatform.Dataverse.models.fetchxml_query","PowerPlatform.Dataverse.models.filters","PowerPlatform.Dataverse.models","PowerPlatform.Dataverse.models.labels","PowerPlatform.Dataverse.models.protocol","PowerPlatform.Dataverse.models.query_builder","PowerPlatform.Dataverse.models.record","PowerPlatform.Dataverse.models.relationship","PowerPlatform.Dataverse.models.table_info","PowerPlatform.Dataverse.models.upsert","PowerPlatform.Dataverse.operations.batch","PowerPlatform.Dataverse.operations.dataframe","PowerPlatform.Dataverse.operations.files","PowerPlatform.Dataverse.operations","PowerPlatform.Dataverse.operations.query","PowerPlatform.Dataverse.operations.records","PowerPlatform.Dataverse.operations.tables","PowerPlatform.Dataverse.utils","PowerPlatform","API Reference","PowerPlatform Dataverse Client SDK"],"titleterms":{"api":33,"appli":12,"attribut":2,"batch":[13,24],"class":[1,4,7,13,14,15,17,18,19,20,21,22,23,24,25,26,28,29,30],"claude_skil":0,"client":[1,34],"common":[2,3],"config":4,"constant":2,"content":[1,2,4,5,7,12,13,14,15,17,18,19,20,21,22,23,24,25,26,28,29,30,34],"core":[4,5,6,7],"data":8,"datafram":25,"datavers":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,34],"error":5,"except":5,"extens":9,"fetchxml_queri":14,"file":26,"filter":15,"function":[12,15],"label":17,"log_config":7,"migrat":[11,12],"migrate_v0_to_v1":12,"model":[13,14,15,16,17,18,19,20,21,22,23],"modul":[1,2,4,5,7,12,13,14,15,17,18,19,20,21,22,23,24,25,26,28,29,30],"oper":[24,25,26,27,28,29,30],"powerplatform":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,34],"protocol":18,"queri":28,"query_build":19,"record":[20,29],"refer":33,"relationship":21,"sdk":34,"submodul":[3,6,10,11,16,27,32],"tabl":30,"table_info":22,"transform":12,"upsert":23,"util":31}}) \ No newline at end of file diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/claude_skill/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/claude_skill/index.rst new file mode 100644 index 00000000..88631626 --- /dev/null +++ b/docs_local/autoapi/PowerPlatform/Dataverse/claude_skill/index.rst @@ -0,0 +1,15 @@ +PowerPlatform.Dataverse.claude_skill +==================================== + +.. py:module:: PowerPlatform.Dataverse.claude_skill + +.. autoapi-nested-parse:: + + Claude Code skill package for the PowerPlatform Dataverse Client SDK. + + This package contains two skills: + - dataverse-sdk-use: Guidance for using the SDK in your applications + - dataverse-sdk-dev: Guidance for developing/contributing to the SDK itself + + + diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/client/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/client/index.rst new file mode 100644 index 00000000..3e3c485f --- /dev/null +++ b/docs_local/autoapi/PowerPlatform/Dataverse/client/index.rst @@ -0,0 +1,157 @@ +PowerPlatform.Dataverse.client +============================== + +.. py:module:: PowerPlatform.Dataverse.client + + +Classes +------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.client.DataverseClient + + +Module Contents +--------------- + +.. py:class:: DataverseClient(base_url: str, credential: azure.core.credentials.TokenCredential, config: Optional[PowerPlatform.Dataverse.core.config.DataverseConfig] = None, *, context: Optional[PowerPlatform.Dataverse.core.config.OperationContext] = None) + + High-level client for Microsoft Dataverse operations. + + This client provides a simple, stable interface for interacting with Dataverse environments + through the Web API. It handles authentication via Azure Identity and delegates HTTP operations + to an internal OData client. + + Key capabilities: + - OData CRUD operations: create, read, update, delete records + - SQL queries: execute read-only SQL via Web API ``?sql`` parameter + - Table metadata: create, inspect, and delete custom tables; create and delete columns + - File uploads: upload files to file columns with chunking support + + :param base_url: Your Dataverse environment URL, for example + ``"https://org.crm.dynamics.com"``. Trailing slash is automatically removed. + :type base_url: :class:`str` + :param credential: Azure Identity credential for authentication. + :type credential: ~azure.core.credentials.TokenCredential + :param config: Optional configuration for language, timeouts, and retries. + If not provided, defaults are loaded from :meth:`~PowerPlatform.Dataverse.core.config.DataverseConfig.from_env`. + :type config: ~PowerPlatform.Dataverse.core.config.DataverseConfig or None + :param context: Optional caller-defined context object appended to the + outbound ``User-Agent`` header for plugin/tool attribution. Cannot be used + together with ``config`` -- pass the context via + :class:`~PowerPlatform.Dataverse.core.config.DataverseConfig` instead. + :type context: ~PowerPlatform.Dataverse.core.config.OperationContext or None + + :raises ValueError: If ``base_url`` is missing or empty after trimming. + :raises ValueError: If both ``config`` and ``context`` are provided. + + .. note:: + The client lazily initializes its internal OData client on first use, allowing lightweight construction without immediate network calls. + + .. note:: + All methods that communicate with the Dataverse Web API may raise + :class:`~PowerPlatform.Dataverse.core.errors.HttpError` on non-successful + HTTP responses (e.g. 401, 403, 404, 429, 500). Individual method + docstrings document only domain-specific exceptions. + + Operations are organized into namespaces: + + - ``client.records`` -- create, update, delete, and get records (single or paginated queries) + - ``client.query`` -- query and search operations + - ``client.tables`` -- table and column metadata management + - ``client.files`` -- file upload operations + - ``client.dataframe`` -- pandas DataFrame wrappers for record CRUD + - ``client.batch`` -- batch multiple operations into a single HTTP request + + The client supports Python's context manager protocol for automatic resource + cleanup and HTTP connection pooling: + + .. rubric:: Example + + **Recommended -- context manager** (enables HTTP connection pooling):: + + from azure.identity import InteractiveBrowserCredential + from PowerPlatform.Dataverse.client import DataverseClient + + credential = InteractiveBrowserCredential() + + with DataverseClient("https://org.crm.dynamics.com", credential) as client: + record_id = client.records.create("account", {"name": "Contoso Ltd"}) + client.records.update("account", record_id, {"telephone1": "555-0100"}) + # Session closed, caches cleared automatically + + **Manual lifecycle**:: + + client = DataverseClient("https://org.crm.dynamics.com", credential) + try: + record_id = client.records.create("account", {"name": "Contoso Ltd"}) + finally: + client.close() + + + .. py:attribute:: auth + + + .. py:attribute:: records + + + .. py:attribute:: query + + + .. py:attribute:: tables + + + .. py:attribute:: files + + + .. py:attribute:: dataframe + + + .. py:attribute:: batch + + + .. py:method:: close() -> None + + Close the client and release resources. + + Closes the HTTP session (if any), clears internal caches, and + marks the client as closed. Safe to call multiple times. After + closing, any operation will raise :class:`RuntimeError`. + + Called automatically when using the client as a context manager. + + Example:: + + client = DataverseClient(base_url, credential) + try: + client.records.create("account", {"name": "Contoso"}) + finally: + client.close() + + + + .. py:method:: flush_cache(kind) -> int + + Flush cached client metadata or state. + + :param kind: Cache kind to flush. Currently supported values: + + - ``"picklist"``: Clears picklist label cache used for label-to-integer conversion + + Future kinds (e.g. ``"entityset"``, ``"primaryid"``) may be added without + breaking this signature. + :type kind: :class:`str` + + :return: Number of cache entries removed. + :rtype: :class:`int` + + .. rubric:: Example + + Clear the picklist cache:: + + removed = client.flush_cache("picklist") + print(f"Cleared {removed} cached picklist entries") + + + diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/common/constants/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/common/constants/index.rst new file mode 100644 index 00000000..b843380a --- /dev/null +++ b/docs_local/autoapi/PowerPlatform/Dataverse/common/constants/index.rst @@ -0,0 +1,77 @@ +PowerPlatform.Dataverse.common.constants +======================================== + +.. py:module:: PowerPlatform.Dataverse.common.constants + +.. autoapi-nested-parse:: + + Constants for Dataverse Web API metadata types. + + These constants define the OData type identifiers used in Web API payloads + for metadata operations. + + + +Attributes +---------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.common.constants.ODATA_TYPE_LOCALIZED_LABEL + PowerPlatform.Dataverse.common.constants.ODATA_TYPE_LABEL + PowerPlatform.Dataverse.common.constants.ODATA_TYPE_LOOKUP_ATTRIBUTE + PowerPlatform.Dataverse.common.constants.ODATA_TYPE_ONE_TO_MANY_RELATIONSHIP + PowerPlatform.Dataverse.common.constants.ODATA_TYPE_MANY_TO_MANY_RELATIONSHIP + PowerPlatform.Dataverse.common.constants.CASCADE_BEHAVIOR_CASCADE + PowerPlatform.Dataverse.common.constants.CASCADE_BEHAVIOR_NO_CASCADE + PowerPlatform.Dataverse.common.constants.CASCADE_BEHAVIOR_REMOVE_LINK + PowerPlatform.Dataverse.common.constants.CASCADE_BEHAVIOR_RESTRICT + + +Module Contents +--------------- + +.. py:data:: ODATA_TYPE_LOCALIZED_LABEL + :value: 'Microsoft.Dynamics.CRM.LocalizedLabel' + + +.. py:data:: ODATA_TYPE_LABEL + :value: 'Microsoft.Dynamics.CRM.Label' + + +.. py:data:: ODATA_TYPE_LOOKUP_ATTRIBUTE + :value: 'Microsoft.Dynamics.CRM.LookupAttributeMetadata' + + +.. py:data:: ODATA_TYPE_ONE_TO_MANY_RELATIONSHIP + :value: 'Microsoft.Dynamics.CRM.OneToManyRelationshipMetadata' + + +.. py:data:: ODATA_TYPE_MANY_TO_MANY_RELATIONSHIP + :value: 'Microsoft.Dynamics.CRM.ManyToManyRelationshipMetadata' + + +.. py:data:: CASCADE_BEHAVIOR_CASCADE + :value: 'Cascade' + + + Perform the action on all referencing table records associated with the referenced table record. + +.. py:data:: CASCADE_BEHAVIOR_NO_CASCADE + :value: 'NoCascade' + + + Do not apply the action to any referencing table records associated with the referenced table record. + +.. py:data:: CASCADE_BEHAVIOR_REMOVE_LINK + :value: 'RemoveLink' + + + Remove the value of the referencing column for all referencing table records when the referenced record is deleted. + +.. py:data:: CASCADE_BEHAVIOR_RESTRICT + :value: 'Restrict' + + + Prevent the referenced table record from being deleted when referencing table records exist. + diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/common/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/common/index.rst new file mode 100644 index 00000000..55ae1b6f --- /dev/null +++ b/docs_local/autoapi/PowerPlatform/Dataverse/common/index.rst @@ -0,0 +1,22 @@ +PowerPlatform.Dataverse.common +============================== + +.. py:module:: PowerPlatform.Dataverse.common + +.. autoapi-nested-parse:: + + Common utilities and constants for the Dataverse SDK. + + This module contains shared constants and utilities used across the SDK. + + + +Submodules +---------- + +.. toctree:: + :maxdepth: 1 + + /autoapi/PowerPlatform/Dataverse/common/constants/index + + diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/core/config/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/core/config/index.rst new file mode 100644 index 00000000..ef0f6d1d --- /dev/null +++ b/docs_local/autoapi/PowerPlatform/Dataverse/core/config/index.rst @@ -0,0 +1,117 @@ +PowerPlatform.Dataverse.core.config +=================================== + +.. py:module:: PowerPlatform.Dataverse.core.config + +.. autoapi-nested-parse:: + + Dataverse client configuration. + + Provides :class:`~PowerPlatform.Dataverse.core.config.DataverseConfig`, a lightweight + immutable container for locale and (reserved) HTTP tuning options plus the + convenience constructor :meth:`~PowerPlatform.Dataverse.core.config.DataverseConfig.from_env`. + + + +Classes +------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.core.config.OperationContext + PowerPlatform.Dataverse.core.config.DataverseConfig + + +Module Contents +--------------- + +.. py:class:: OperationContext + + Caller-defined context appended to outbound ``User-Agent`` headers. + + The context string is validated to be semicolon-separated ``key=value`` pairs + using only allowed keys (``app``, ``skill``, ``agent``) with values from + closed allowlists. Free-form text, email addresses, PII, and unknown keys + are rejected. + + :param user_agent_context: Attribution string in ``key=value;key=value`` format. + :type user_agent_context: :class:`str` + + :raises ValueError: If the string is empty, contains control characters, + does not match the required ``key=value`` format, or uses unknown + keys/values. + + + .. py:attribute:: user_agent_context + :type: str + + +.. py:class:: DataverseConfig + + Configuration settings for Dataverse client operations. + + :param language_code: LCID (Locale ID) for localized labels and messages. Default is 1033 (English - United States). + :type language_code: :class:`int` + :param http_retries: Optional maximum number of retry attempts for transient HTTP errors. Reserved for future use. + :type http_retries: :class:`int` or None + :param http_backoff: Optional backoff multiplier (in seconds) between retry attempts. Reserved for future use. + :type http_backoff: :class:`float` or None + :param http_timeout: Optional request timeout in seconds. Reserved for future use. + :type http_timeout: :class:`float` or None + :param log_config: Optional local HTTP diagnostics logging configuration. + When provided, all HTTP requests and responses are logged to timestamped + ``.log`` files with automatic redaction of sensitive headers. + :type log_config: ~PowerPlatform.Dataverse.core.log_config.LogConfig or None + :param operation_context: Optional caller-defined context object appended to the + outbound ``User-Agent`` header as a parenthesized comment. Intended for + plugin/tool attribution. + :type operation_context: ~PowerPlatform.Dataverse.core.config.OperationContext or None + + + .. py:attribute:: language_code + :type: int + :value: 1033 + + + + .. py:attribute:: http_retries + :type: Optional[int] + :value: None + + + + .. py:attribute:: http_backoff + :type: Optional[float] + :value: None + + + + .. py:attribute:: http_timeout + :type: Optional[float] + :value: None + + + + .. py:attribute:: log_config + :type: Optional[PowerPlatform.Dataverse.core.log_config.LogConfig] + :value: None + + + + .. py:attribute:: operation_context + :type: Optional[OperationContext] + :value: None + + + + .. py:method:: from_env() -> DataverseConfig + :classmethod: + + + Create a configuration instance with default settings. + + :return: Configuration instance with default values. + :rtype: ~PowerPlatform.Dataverse.core.config.DataverseConfig + + + diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/core/errors/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/core/errors/index.rst new file mode 100644 index 00000000..7fb487a0 --- /dev/null +++ b/docs_local/autoapi/PowerPlatform/Dataverse/core/errors/index.rst @@ -0,0 +1,185 @@ +PowerPlatform.Dataverse.core.errors +=================================== + +.. py:module:: PowerPlatform.Dataverse.core.errors + +.. autoapi-nested-parse:: + + Structured Dataverse exception hierarchy. + + This module provides :class:`~PowerPlatform.Dataverse.core.errors.DataverseError` and + specialized :class:`~PowerPlatform.Dataverse.core.errors.ValidationError`, + :class:`~PowerPlatform.Dataverse.core.errors.MetadataError`, + :class:`~PowerPlatform.Dataverse.core.errors.SQLParseError`, and + :class:`~PowerPlatform.Dataverse.core.errors.HttpError` for validation, metadata, + SQL parsing, and Web API HTTP failures. + + + +Exceptions +---------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.core.errors.DataverseError + PowerPlatform.Dataverse.core.errors.ValidationError + PowerPlatform.Dataverse.core.errors.MetadataError + PowerPlatform.Dataverse.core.errors.SQLParseError + PowerPlatform.Dataverse.core.errors.HttpError + + +Module Contents +--------------- + +.. py:exception:: DataverseError(message: str, code: str, subcode: Optional[str] = None, status_code: Optional[int] = None, details: Optional[Dict[str, Any]] = None, source: Optional[str] = None, is_transient: bool = False) + + Bases: :py:obj:`Exception` + + + Base structured exception for the Dataverse SDK. + + :param message: Human-readable error message. + :type message: :class:`str` + :param code: Error category code (e.g. ``"validation_error"``, ``"http_error"``). + :type code: :class:`str` + :param subcode: Optional subcategory or specific error identifier. + :type subcode: :class:`str` | None + :param status_code: Optional HTTP status code if the error originated from an HTTP response. + :type status_code: :class:`int` | None + :param details: Optional dictionary containing additional diagnostic information. + :type details: :class:`dict` | None + :param source: Error source, either ``"client"`` or ``"server"``. + :type source: :class:`str` + :param is_transient: Whether the error is potentially transient and may succeed on retry. + :type is_transient: :class:`bool` + + Initialize self. See help(type(self)) for accurate signature. + + + .. py:attribute:: message + + + .. py:attribute:: code + + + .. py:attribute:: subcode + :value: None + + + + .. py:attribute:: status_code + :value: None + + + + .. py:attribute:: details + + + .. py:attribute:: source + :value: 'client' + + + + .. py:attribute:: is_transient + :value: False + + + + .. py:attribute:: timestamp + + + .. py:method:: to_dict() -> Dict[str, Any] + + Convert the error to a dictionary representation. + + :return: Dictionary containing all error properties. + :rtype: :class:`dict` + + + +.. py:exception:: ValidationError(message: str, *, subcode: Optional[str] = None, details: Optional[Dict[str, Any]] = None) + + Bases: :py:obj:`DataverseError` + + + Exception raised for client-side validation failures. + + :param message: Human-readable validation error message. + :type message: :class:`str` + :param subcode: Optional specific validation error identifier. + :type subcode: :class:`str` | None + :param details: Optional dictionary with additional validation context. + :type details: :class:`dict` | None + + Initialize self. See help(type(self)) for accurate signature. + + +.. py:exception:: MetadataError(message: str, *, subcode: Optional[str] = None, details: Optional[Dict[str, Any]] = None) + + Bases: :py:obj:`DataverseError` + + + Exception raised for metadata operation failures. + + :param message: Human-readable metadata error message. + :type message: :class:`str` + :param subcode: Optional specific metadata error identifier. + :type subcode: :class:`str` | None + :param details: Optional dictionary with additional metadata context. + :type details: :class:`dict` | None + + Initialize self. See help(type(self)) for accurate signature. + + +.. py:exception:: SQLParseError(message: str, *, subcode: Optional[str] = None, details: Optional[Dict[str, Any]] = None) + + Bases: :py:obj:`DataverseError` + + + Exception raised for SQL query parsing failures. + + :param message: Human-readable SQL parsing error message. + :type message: :class:`str` + :param subcode: Optional specific SQL parsing error identifier. + :type subcode: :class:`str` | None + :param details: Optional dictionary with SQL query context and parse information. + :type details: :class:`dict` | None + + Initialize self. See help(type(self)) for accurate signature. + + +.. py:exception:: HttpError(message: str, status_code: int, is_transient: bool = False, subcode: Optional[str] = None, service_error_code: Optional[str] = None, correlation_id: Optional[str] = None, client_request_id: Optional[str] = None, service_request_id: Optional[str] = None, traceparent: Optional[str] = None, body_excerpt: Optional[str] = None, retry_after: Optional[int] = None, details: Optional[Dict[str, Any]] = None) + + Bases: :py:obj:`DataverseError` + + + Exception raised for HTTP request failures from the Dataverse Web API. + + :param message: Human-readable HTTP error message, typically from the API error response. + :type message: :class:`str` + :param status_code: HTTP status code (e.g. 400, 404, 500). + :type status_code: :class:`int` + :param is_transient: Whether the error is transient (429, 503, 504) and may succeed on retry. + :type is_transient: :class:`bool` + :param subcode: Optional HTTP status category (e.g. ``"4xx"``, ``"5xx"``). + :type subcode: :class:`str` | None + :param service_error_code: Optional Dataverse-specific error code from the API response. + :type service_error_code: :class:`str` | None + :param correlation_id: Optional client-generated correlation ID for tracking requests within an SDK call. + :type correlation_id: :class:`str` | None + :param client_request_id: Optional client-generated request ID injected into outbound headers. + :type client_request_id: :class:`str` | None + :param service_request_id: Optional ``x-ms-service-request-id`` value returned by Dataverse servers. + :type service_request_id: :class:`str` | None + :param traceparent: Optional W3C trace context for distributed tracing. + :type traceparent: :class:`str` | None + :param body_excerpt: Optional excerpt of the response body for diagnostics. + :type body_excerpt: :class:`str` | None + :param retry_after: Optional number of seconds to wait before retrying (from Retry-After header). + :type retry_after: :class:`int` | None + :param details: Optional additional diagnostic details. + :type details: :class:`dict` | None + + Initialize self. See help(type(self)) for accurate signature. + + diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/core/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/core/index.rst new file mode 100644 index 00000000..31a27202 --- /dev/null +++ b/docs_local/autoapi/PowerPlatform/Dataverse/core/index.rst @@ -0,0 +1,25 @@ +PowerPlatform.Dataverse.core +============================ + +.. py:module:: PowerPlatform.Dataverse.core + +.. autoapi-nested-parse:: + + Core infrastructure components for the Dataverse SDK. + + This module contains the foundational components including authentication, + configuration, HTTP client, and error handling. + + + +Submodules +---------- + +.. toctree:: + :maxdepth: 1 + + /autoapi/PowerPlatform/Dataverse/core/config/index + /autoapi/PowerPlatform/Dataverse/core/errors/index + /autoapi/PowerPlatform/Dataverse/core/log_config/index + + diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/core/log_config/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/core/log_config/index.rst new file mode 100644 index 00000000..aa4a5e09 --- /dev/null +++ b/docs_local/autoapi/PowerPlatform/Dataverse/core/log_config/index.rst @@ -0,0 +1,90 @@ +PowerPlatform.Dataverse.core.log_config +======================================= + +.. py:module:: PowerPlatform.Dataverse.core.log_config + +.. autoapi-nested-parse:: + + Local file logging configuration for Dataverse SDK HTTP diagnostics. + + Provides :class:`~PowerPlatform.Dataverse.core.log_config.LogConfig`, an opt-in configuration for writing request/response + traces to ``.log`` files with automatic header redaction and timestamped filenames. + + + +Classes +------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.core.log_config.LogConfig + + +Module Contents +--------------- + +.. py:class:: LogConfig + + Configuration for local HTTP diagnostics logging. + + When provided to :class:`~PowerPlatform.Dataverse.client.DataverseClient` via + :class:`~PowerPlatform.Dataverse.core.config.DataverseConfig`, every HTTP request + and response is logged to timestamped ``.log`` files in the specified folder. + Sensitive headers (e.g. ``Authorization``) are automatically redacted. + + :param log_folder: Directory path for log files. Created automatically if missing. + Default: ``"./dataverse_logs"`` + :param log_file_prefix: Filename prefix. Timestamp is appended automatically. + Default: ``"dataverse"`` → ``dataverse_20260310_143022.log`` + :param max_body_bytes: Maximum bytes of request/response body to capture. + ``0`` (default) disables body capture. Enable only for active debugging + sessions — bodies may contain PII and sensitive business data. + :param redacted_headers: Header names (case-insensitive) whose values are + replaced with ``"[REDACTED]"`` in logs. Defaults include + ``Authorization``, ``Proxy-Authorization``, etc. + :param log_level: Python logging level name. Default: ``"DEBUG"``. + :param max_file_bytes: Max size per log file before rotation (bytes). + Default: ``10_485_760`` (10 MB). + :param backup_count: Number of rotated backup files to keep. Default: ``5``. + + + .. py:attribute:: log_folder + :type: str + :value: './dataverse_logs' + + + + .. py:attribute:: log_file_prefix + :type: str + :value: 'dataverse' + + + + .. py:attribute:: max_body_bytes + :type: int + :value: 0 + + + + .. py:attribute:: redacted_headers + :type: FrozenSet[str] + + + .. py:attribute:: log_level + :type: str + :value: 'DEBUG' + + + + .. py:attribute:: max_file_bytes + :type: int + :value: 10485760 + + + + .. py:attribute:: backup_count + :type: int + :value: 5 + + + diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/data/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/data/index.rst new file mode 100644 index 00000000..62e70f16 --- /dev/null +++ b/docs_local/autoapi/PowerPlatform/Dataverse/data/index.rst @@ -0,0 +1,14 @@ +PowerPlatform.Dataverse.data +============================ + +.. py:module:: PowerPlatform.Dataverse.data + +.. autoapi-nested-parse:: + + Data access layer for the Dataverse SDK. + + This module contains OData protocol handling, CRUD operations, metadata management, + SQL query functionality, and file upload capabilities. + + + diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/extensions/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/extensions/index.rst new file mode 100644 index 00000000..4f88c87c --- /dev/null +++ b/docs_local/autoapi/PowerPlatform/Dataverse/extensions/index.rst @@ -0,0 +1,11 @@ +PowerPlatform.Dataverse.extensions +================================== + +.. py:module:: PowerPlatform.Dataverse.extensions + +.. autoapi-nested-parse:: + + Optional extensions for the Dataverse SDK. Currently a placeholder. + + + diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/index.rst new file mode 100644 index 00000000..8f38b9f8 --- /dev/null +++ b/docs_local/autoapi/PowerPlatform/Dataverse/index.rst @@ -0,0 +1,24 @@ +PowerPlatform.Dataverse +======================= + +.. py:module:: PowerPlatform.Dataverse + + +Submodules +---------- + +.. toctree:: + :maxdepth: 1 + + /autoapi/PowerPlatform/Dataverse/claude_skill/index + /autoapi/PowerPlatform/Dataverse/client/index + /autoapi/PowerPlatform/Dataverse/common/index + /autoapi/PowerPlatform/Dataverse/core/index + /autoapi/PowerPlatform/Dataverse/data/index + /autoapi/PowerPlatform/Dataverse/extensions/index + /autoapi/PowerPlatform/Dataverse/migration/index + /autoapi/PowerPlatform/Dataverse/models/index + /autoapi/PowerPlatform/Dataverse/operations/index + /autoapi/PowerPlatform/Dataverse/utils/index + + diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/migration/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/migration/index.rst new file mode 100644 index 00000000..15bc1e31 --- /dev/null +++ b/docs_local/autoapi/PowerPlatform/Dataverse/migration/index.rst @@ -0,0 +1,15 @@ +PowerPlatform.Dataverse.migration +================================= + +.. py:module:: PowerPlatform.Dataverse.migration + + +Submodules +---------- + +.. toctree:: + :maxdepth: 1 + + /autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index + + diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index.rst new file mode 100644 index 00000000..0e6e73c2 --- /dev/null +++ b/docs_local/autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index.rst @@ -0,0 +1,119 @@ +PowerPlatform.Dataverse.migration.migrate_v0_to_v1 +================================================== + +.. py:module:: PowerPlatform.Dataverse.migration.migrate_v0_to_v1 + +.. autoapi-nested-parse:: + + DV-Python-SDK v0 -> v1 GA migration codemod. + + Mechanically rewrites beta (0.1.0b*) call sites to their GA (1.0) equivalents + using LibCST (concrete syntax tree — preserves all whitespace and comments). + + Usage:: + + pip install PowerPlatform-Dataverse-Client[migration] + dataverse-migrate path/to/your/scripts/ + dataverse-migrate path/to/your/scripts/ --dry-run # preview without writing + dataverse-migrate path/to/your/scripts/ --client-var=svc # if client is named 'svc' + + # Or via module for development installs: + python -m PowerPlatform.Dataverse.migration.migrate_v0_to_v1 path/to/your/scripts/ + + Transformations applied + ----------------------- + Builder methods (.filter_* -> .where(col(...)...)):: + + .filter_eq("col", v) -> .where(col("col") == v) + .filter_ne("col", v) -> .where(col("col") != v) + .filter_gt("col", v) -> .where(col("col") > v) + .filter_ge("col", v) -> .where(col("col") >= v) + .filter_lt("col", v) -> .where(col("col") < v) + .filter_le("col", v) -> .where(col("col") <= v) + .filter_contains("col", v) -> .where(col("col").contains(v)) + .filter_startswith("col", v) -> .where(col("col").startswith(v)) + .filter_endswith("col", v) -> .where(col("col").endswith(v)) + .filter_in("col", vals) -> .where(col("col").in_(vals)) + .filter_not_in("col", vals) -> .where(col("col").not_in(vals)) + .filter_null("col") -> .where(col("col").is_null()) + .filter_not_null("col") -> .where(col("col").is_not_null()) + .filter_between("col", lo, hi) -> .where(col("col").between(lo, hi)) + .filter_not_between("col", lo, hi) -> .where(col("col").not_between(lo, hi)) + .filter_raw("expr") -> .where(raw("expr")) + .filter("expr") -> .where(raw("expr")) + .execute(by_page=True) -> .execute_pages() + .execute(by_page=False) -> .execute() (flag removed) + .to_dataframe() -> .execute().to_dataframe() + Inserts .execute() when the receiver is a recognised QueryBuilder chain + (contains .builder(), .select(), .where(), or a .filter_*() call). + + Record namespace:: + + batch.records.get(t, id) -> batch.records.retrieve(t, id) + + Top-level shortcuts (removed at GA):: + + client.create(t, d) -> client.records.create(t, d) + client.update(t, id, d) -> client.records.update(t, id, d) + client.delete(t, id) -> client.records.delete(t, id) + client.get(t, id) -> client.records.get(t, id) [deprecated; see manual section] + client.query_sql(sql) -> client.query.sql(sql) + client.get_table_info(t) -> client.tables.get(t) + client.create_table(t, …) -> client.tables.create(t, …) + client.delete_table(t) -> client.tables.delete(t) + client.list_tables() -> client.tables.list() + client.create_columns(t, …) -> client.tables.add_columns(t, …) + client.delete_columns(t, …) -> client.tables.remove_columns(t, …) + client.upload_file(…) -> client.files.upload(…) + + Import management: + Adds ``from PowerPlatform.Dataverse.models.filters import col`` when a + .filter_* method is rewritten (if col is not already imported). + Adds ``raw`` to the same import when .filter_raw or .filter is rewritten. + + NOT handled by this codemod (manual migration required): + execute(by_page=variable) -> manual review required (variable argument, not literal) + client.records.get(t, id) -> client.records.retrieve(t, id) + Return type changes: beta returns Record (raises on 404); GA retrieve() returns + Record | None. Callers that do not guard against None will fail silently. + client.records.get(t, kw=…) -> client.records.list(t, kw=…) + Return type changes: beta returns Iterable[List[Record]] (pages); GA list() + returns QueryResult (flat iterable over Records). Any ``for page in result: + for rec in page:`` iteration pattern breaks after a mechanical rename. + client.dataframe.get() -> client.query.builder(…).execute().to_dataframe() + Expression reconstruction requires understanding caller intent. + client.query.sql_select()/sql_join()/sql_joins() -> removed (no mechanical replacement) + + + +Functions +--------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.migration.migrate_v0_to_v1.find_manual_patterns + PowerPlatform.Dataverse.migration.migrate_v0_to_v1.migrate_source + PowerPlatform.Dataverse.migration.migrate_v0_to_v1.migrate_file + PowerPlatform.Dataverse.migration.migrate_v0_to_v1.main + + +Module Contents +--------------- + +.. py:function:: find_manual_patterns(source: str, *, client_var: str = 'client') -> List[str] + + Return descriptions of patterns in *source* that require manual migration. + + +.. py:function:: migrate_source(source: str, *, client_var: str = 'client') -> str + + Parse *source*, apply transformations, return migrated source. + + +.. py:function:: migrate_file(path: pathlib.Path, *, dry_run: bool = False, client_var: str = 'client') -> Tuple[bool, List[str]] + + Migrate *path* in place. Returns (was_changed, manual_review_notes). + + +.. py:function:: main(argv: Optional[List[str]] = None) -> int + diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/models/batch/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/models/batch/index.rst new file mode 100644 index 00000000..a0d3d118 --- /dev/null +++ b/docs_local/autoapi/PowerPlatform/Dataverse/models/batch/index.rst @@ -0,0 +1,154 @@ +PowerPlatform.Dataverse.models.batch +==================================== + +.. py:module:: PowerPlatform.Dataverse.models.batch + +.. autoapi-nested-parse:: + + Public result types for batch operations. + + + +Classes +------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.models.batch.BatchItemResponse + PowerPlatform.Dataverse.models.batch.BatchResult + + +Module Contents +--------------- + +.. py:class:: BatchItemResponse + + Response from a single operation within a batch request. + + Responses are returned in submission order. For operations added to a + changeset, responses appear in the changeset's position in that order. + + :param status_code: HTTP status code for this operation (e.g. 204, 200, 400). + :param content_id: ``Content-ID`` value from the changeset response part, if any. + :param entity_id: GUID extracted from the ``OData-EntityId`` response header. + Set for successful create (POST) operations. + :param data: Parsed JSON response body (e.g. for GET operations). + :param error_message: Error message when the operation failed. + :param error_code: Service error code when the operation failed. + + Example:: + + for item in result.responses: + if item.is_success: + print(f"[OK] {item.status_code} entity_id={item.entity_id}") + else: + print(f"[ERR] {item.status_code}: {item.error_message}") + + + .. py:attribute:: status_code + :type: int + + + .. py:attribute:: content_id + :type: Optional[str] + :value: None + + + + .. py:attribute:: entity_id + :type: Optional[str] + :value: None + + + + .. py:attribute:: data + :type: Optional[Dict[str, Any]] + :value: None + + + + .. py:attribute:: error_message + :type: Optional[str] + :value: None + + + + .. py:attribute:: error_code + :type: Optional[str] + :value: None + + + + .. py:property:: is_success + :type: bool + + + Return True when status_code is 2xx. + + +.. py:class:: BatchResult + + Result of executing a batch request. + + Contains one :class:`BatchItemResponse` per HTTP operation submitted. + Operations that expand to multiple HTTP requests (e.g. ``add_columns`` + with three columns) contribute three entries. + + :param responses: All responses in submission order. + + Example:: + + result = client.batch.new().execute() + print(f"Succeeded: {len(result.succeeded)}, Failed: {len(result.failed)}") + for guid in result.entity_ids: + print(f"[OK] entity_id: {guid}") + + + .. py:attribute:: responses + :type: List[BatchItemResponse] + :value: [] + + + + .. py:property:: succeeded + :type: List[BatchItemResponse] + + + Responses with 2xx status codes. + + + .. py:property:: failed + :type: List[BatchItemResponse] + + + Responses with non-2xx status codes. + + + .. py:property:: has_errors + :type: bool + + + True when any response has a non-2xx status code. + + + .. py:property:: entity_ids + :type: List[str] + + + GUIDs extracted from ``OData-EntityId`` headers of successful responses. + + Returns entity IDs from any successful (2xx) response that includes an + ``OData-EntityId`` header. Both individual ``POST`` (create) and + ``PATCH`` (update) operations return this header with the record's GUID. + ``GET`` and ``DELETE`` operations do not. + + .. note:: + ``CreateMultiple`` and ``UpsertMultiple`` action responses do **not** + return per-record ``OData-EntityId`` headers. Their IDs are in the + JSON response body (``data["Ids"]``). Access them via:: + + for resp in result.succeeded: + if resp.data and "Ids" in resp.data: + bulk_ids = resp.data["Ids"] + + diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index.rst new file mode 100644 index 00000000..1632cc4a --- /dev/null +++ b/docs_local/autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index.rst @@ -0,0 +1,76 @@ +PowerPlatform.Dataverse.models.fetchxml_query +============================================= + +.. py:module:: PowerPlatform.Dataverse.models.fetchxml_query + +.. autoapi-nested-parse:: + + FetchXmlQuery — inert query object returned by QueryOperations.fetchxml(). + + + +Classes +------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.models.fetchxml_query.FetchXmlQuery + + +Module Contents +--------------- + +.. py:class:: FetchXmlQuery(xml: str, entity_name: str, client: PowerPlatform.Dataverse.client.DataverseClient) + + Inert FetchXML query object. No HTTP request is made until + :meth:`execute` or :meth:`execute_pages` is called. + + Obtained via ``client.query.fetchxml(xml)``. + + :param xml: Stripped, well-formed FetchXML string. + :param entity_name: Entity schema name from the ```` element. + :param client: Parent :class:`~PowerPlatform.Dataverse.client.DataverseClient`. + + + .. py:method:: execute() -> PowerPlatform.Dataverse.models.record.QueryResult + + Execute the FetchXML query and return all results as a :class:`~PowerPlatform.Dataverse.models.record.QueryResult`. + + Blocking — fetches all pages upfront and holds every record in memory before + returning. Simple for small-to-medium result sets; use :meth:`execute_pages` + when the result set may be large or you want to process records as they arrive. + + :return: All matching records across all pages. + :rtype: :class:`~PowerPlatform.Dataverse.models.record.QueryResult` + + Example:: + + rows = client.query.fetchxml(xml).execute() + df = rows.to_dataframe() + + + + .. py:method:: execute_pages() -> Iterator[PowerPlatform.Dataverse.models.record.QueryResult] + + Lazily yield one :class:`~PowerPlatform.Dataverse.models.record.QueryResult` per HTTP page. + + Streaming — each iteration fires one HTTP request and yields one page. + Prefer over :meth:`execute` when: + + - The result set may be large and you do not want all records in memory at once. + - You want early exit: stop iterating once you find what you need and the + remaining HTTP round-trips are skipped automatically. + - You need per-page progress reporting or batched downstream writes. + + One-shot — do not iterate more than once. + + :return: Iterator of per-page :class:`~PowerPlatform.Dataverse.models.record.QueryResult` objects. + :rtype: Iterator[:class:`~PowerPlatform.Dataverse.models.record.QueryResult`] + + Example:: + + for page in client.query.fetchxml(xml).execute_pages(): + process(page.to_dataframe()) + + + diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/models/filters/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/models/filters/index.rst new file mode 100644 index 00000000..1e85b977 --- /dev/null +++ b/docs_local/autoapi/PowerPlatform/Dataverse/models/filters/index.rst @@ -0,0 +1,365 @@ +PowerPlatform.Dataverse.models.filters +====================================== + +.. py:module:: PowerPlatform.Dataverse.models.filters + +.. autoapi-nested-parse:: + + Composable OData filter expressions for the Dataverse SDK. + + Provides an expression tree that compiles to OData ``$filter`` strings, + with Python operator overloads (``&``, ``|``, ``~``) for composing + complex filter conditions. + + Example:: + + from PowerPlatform.Dataverse.models.filters import col, raw + + # Preferred GA idiom — col() proxy + expr = col("statecode") == 0 + print(expr.to_odata()) # statecode eq 0 + + # Complex composition with OR and AND + expr = (col("statecode") == 0) | (col("statecode") == 1) & (col("revenue") > 100000) + print(expr.to_odata()) + + # In / not-in + expr = col("statecode").in_([0, 1, 2]) + print(expr.to_odata()) + # Microsoft.Dynamics.CRM.In(PropertyName='statecode',PropertyValues=["0","1","2"]) + + # Raw OData escape hatch (no deprecation warning) + expr = raw("Microsoft.Dynamics.CRM.Today(PropertyName='createdon')") + + # Negation + expr = ~(col("statecode") == 1) + print(expr.to_odata()) # not (statecode eq 1) + + + +Classes +------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.models.filters.FilterExpression + PowerPlatform.Dataverse.models.filters.ColumnProxy + + +Functions +--------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.models.filters.col + PowerPlatform.Dataverse.models.filters.raw + PowerPlatform.Dataverse.models.filters.eq + PowerPlatform.Dataverse.models.filters.ne + PowerPlatform.Dataverse.models.filters.gt + PowerPlatform.Dataverse.models.filters.ge + PowerPlatform.Dataverse.models.filters.lt + PowerPlatform.Dataverse.models.filters.le + PowerPlatform.Dataverse.models.filters.contains + PowerPlatform.Dataverse.models.filters.startswith + PowerPlatform.Dataverse.models.filters.endswith + PowerPlatform.Dataverse.models.filters.between + PowerPlatform.Dataverse.models.filters.is_null + PowerPlatform.Dataverse.models.filters.is_not_null + PowerPlatform.Dataverse.models.filters.filter_in + PowerPlatform.Dataverse.models.filters.not_in + PowerPlatform.Dataverse.models.filters.not_between + + +Module Contents +--------------- + +.. py:class:: FilterExpression + + Base class for composable OData filter expressions. + + Supports Python operator overloads for logical composition: + + - ``expr1 & expr2`` produces ``(expr1 and expr2)`` + - ``expr1 | expr2`` produces ``(expr1 or expr2)`` + - ``~expr`` produces ``not (expr)`` + + + .. py:method:: to_odata() -> str + :abstractmethod: + + + Compile this expression to an OData ``$filter`` string. + + + +.. py:class:: ColumnProxy(name: str) + + Fluent proxy for building OData filter expressions from a column name. + + Returned by :func:`col`. Operator overloads and methods produce + :class:`FilterExpression` instances that can be passed to + ``QueryBuilder.where()``. + + Example:: + + from PowerPlatform.Dataverse.models.filters import col + + expr = col("statecode") == 0 # equality + expr = col("revenue") > 1_000_000 # comparison + expr = col("name").like("Contoso%") # startswith + expr = col("name").is_null() # null check + expr = col("statecode").in_([0, 1]) # in + + + .. py:method:: is_null() -> FilterExpression + + Column equals null: ``column eq null``. + + + + .. py:method:: is_not_null() -> FilterExpression + + Column not null: ``column ne null``. + + + + .. py:method:: in_(values: Collection[Any]) -> FilterExpression + + In filter using ``Microsoft.Dynamics.CRM.In``. + + :param values: Non-empty collection of values. + :raises ValueError: If ``values`` is empty. + + + + .. py:method:: not_in(values: Collection[Any]) -> FilterExpression + + Not-in filter using ``Microsoft.Dynamics.CRM.NotIn``. + + :param values: Non-empty collection of values. + :raises ValueError: If ``values`` is empty. + + + + .. py:method:: between(lo: Any, hi: Any) -> FilterExpression + + Between filter: ``(column ge lo and column le hi)``. + + + + .. py:method:: not_between(lo: Any, hi: Any) -> FilterExpression + + Not-between filter: ``not (column ge lo and column le hi)``. + + + + .. py:method:: contains(value: str) -> FilterExpression + + Contains filter: ``contains(column, value)``. + + + + .. py:method:: startswith(value: str) -> FilterExpression + + Startswith filter: ``startswith(column, value)``. + + + + .. py:method:: endswith(value: str) -> FilterExpression + + Endswith filter: ``endswith(column, value)``. + + + + .. py:method:: like(pattern: str) -> FilterExpression + + Pattern-match filter compiled to the closest OData equivalent. + + +-----------------+-----------------------------+-------------------------------------+ + | Pattern form | Example | Compiles to | + +=================+=============================+=====================================+ + | ``val%`` | ``like("Contoso%")`` | ``startswith(column,'Contoso')`` | + +-----------------+-----------------------------+-------------------------------------+ + | ``%val`` | ``like("%Ltd")`` | ``endswith(column,'Ltd')`` | + +-----------------+-----------------------------+-------------------------------------+ + | ``%val%`` | ``like("%Corp%")`` | ``contains(column,'Corp')`` | + +-----------------+-----------------------------+-------------------------------------+ + | No wildcard | ``like("Contoso")`` | ``column eq 'Contoso'`` | + +-----------------+-----------------------------+-------------------------------------+ + | Other | ``like("Con%oso")`` | :class:`ValueError` | + +-----------------+-----------------------------+-------------------------------------+ + + :param pattern: LIKE-style pattern string. + :raises ValueError: If the pattern cannot be reduced to a single OData function. + + + + .. py:method:: not_like(pattern: str) -> FilterExpression + + Negated pattern-match filter; mirrors :meth:`like` rules then negates. + + :param pattern: LIKE-style pattern string (same rules as :meth:`like`). + :raises ValueError: If the pattern cannot be reduced to a single OData function. + + + +.. py:function:: col(name: str) -> ColumnProxy + + Return a :class:`ColumnProxy` for building filter expressions. + + This is the preferred GA idiom for constructing filter expressions:: + + from PowerPlatform.Dataverse.models.filters import col + + expr = col("statecode") == 0 + expr = col("revenue") > 1_000_000 + expr = col("name").like("Contoso%") + expr = col("statecode").in_([0, 1]) + expr = col("parentaccountid").is_null() + + :param name: Column logical name (case-insensitive, will be lowercased). + :return: A :class:`ColumnProxy` bound to the column. + :raises ValueError: If ``name`` is empty. + + +.. py:function:: raw(filter_string: str) -> FilterExpression + + Verbatim OData filter expression (passed through unchanged). + + This function is **not** deprecated — it is the OData escape hatch with + no typed replacement. + + :param filter_string: Raw OData filter string. + :return: A :class:`FilterExpression`. + + Example:: + + raw("Microsoft.Dynamics.CRM.Today(PropertyName='createdon')") + + +.. py:function:: eq(column: str, value: Any) -> FilterExpression + + Equality filter: ``column eq value``. + + .. deprecated:: + Use ``col(column) == value`` instead. + + +.. py:function:: ne(column: str, value: Any) -> FilterExpression + + Not-equal filter: ``column ne value``. + + .. deprecated:: + Use ``col(column) != value`` instead. + + +.. py:function:: gt(column: str, value: Any) -> FilterExpression + + Greater-than filter: ``column gt value``. + + .. deprecated:: + Use ``col(column) > value`` instead. + + +.. py:function:: ge(column: str, value: Any) -> FilterExpression + + Greater-than-or-equal filter: ``column ge value``. + + .. deprecated:: + Use ``col(column) >= value`` instead. + + +.. py:function:: lt(column: str, value: Any) -> FilterExpression + + Less-than filter: ``column lt value``. + + .. deprecated:: + Use ``col(column) < value`` instead. + + +.. py:function:: le(column: str, value: Any) -> FilterExpression + + Less-than-or-equal filter: ``column le value``. + + .. deprecated:: + Use ``col(column) <= value`` instead. + + +.. py:function:: contains(column: str, value: str) -> FilterExpression + + Contains filter: ``contains(column, value)``. + + .. deprecated:: + Use ``col(column).contains(value)`` instead. + + +.. py:function:: startswith(column: str, value: str) -> FilterExpression + + Startswith filter: ``startswith(column, value)``. + + .. deprecated:: + Use ``col(column).startswith(value)`` instead. + + +.. py:function:: endswith(column: str, value: str) -> FilterExpression + + Endswith filter: ``endswith(column, value)``. + + .. deprecated:: + Use ``col(column).endswith(value)`` instead. + + +.. py:function:: between(column: str, low: Any, high: Any) -> FilterExpression + + Between filter: ``(column ge low and column le high)``. + + .. deprecated:: + Use ``col(column).between(low, high)`` instead. + + +.. py:function:: is_null(column: str) -> FilterExpression + + Null check: ``column eq null``. + + .. deprecated:: + Use ``col(column).is_null()`` instead. + + +.. py:function:: is_not_null(column: str) -> FilterExpression + + Not-null check: ``column ne null``. + + .. deprecated:: + Use ``col(column).is_not_null()`` instead. + + +.. py:function:: filter_in(column: str, values: Collection[Any]) -> FilterExpression + + In filter using ``Microsoft.Dynamics.CRM.In``. + + Named ``filter_in`` because ``in`` is a Python keyword. + + .. deprecated:: + Use ``col(column).in_(values)`` instead. + + :raises ValueError: If ``values`` is empty. + + +.. py:function:: not_in(column: str, values: Collection[Any]) -> FilterExpression + + Not-in filter using ``Microsoft.Dynamics.CRM.NotIn``. + + .. deprecated:: + Use ``col(column).not_in(values)`` instead. + + :raises ValueError: If ``values`` is empty. + + +.. py:function:: not_between(column: str, low: Any, high: Any) -> FilterExpression + + Not-between filter: ``not (column ge low and column le high)``. + + .. deprecated:: + Use ``col(column).not_between(low, high)`` instead. + + diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/models/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/models/index.rst new file mode 100644 index 00000000..b7ae9520 --- /dev/null +++ b/docs_local/autoapi/PowerPlatform/Dataverse/models/index.rst @@ -0,0 +1,41 @@ +PowerPlatform.Dataverse.models +============================== + +.. py:module:: PowerPlatform.Dataverse.models + +.. autoapi-nested-parse:: + + Data models and type definitions for the Dataverse SDK. + + Provides dataclasses and helpers for Dataverse entities: + + - :class:`~PowerPlatform.Dataverse.models.query_builder.QueryBuilder`: Fluent query builder. + - :mod:`~PowerPlatform.Dataverse.models.filters`: Composable OData filter expressions + via :func:`~PowerPlatform.Dataverse.models.filters.col` and + :func:`~PowerPlatform.Dataverse.models.filters.raw`. + - :class:`~PowerPlatform.Dataverse.models.record.QueryResult`: Iterable result wrapper. + - :class:`~PowerPlatform.Dataverse.models.record.Record`: Dataverse entity record. + - :class:`~PowerPlatform.Dataverse.models.upsert.UpsertItem`: Upsert operation item. + - :class:`~PowerPlatform.Dataverse.models.fetchxml_query.FetchXmlQuery`: FetchXML query object. + - :class:`~PowerPlatform.Dataverse.models.protocol.DataverseModel`: Typed-model protocol. + + + +Submodules +---------- + +.. toctree:: + :maxdepth: 1 + + /autoapi/PowerPlatform/Dataverse/models/batch/index + /autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index + /autoapi/PowerPlatform/Dataverse/models/filters/index + /autoapi/PowerPlatform/Dataverse/models/labels/index + /autoapi/PowerPlatform/Dataverse/models/protocol/index + /autoapi/PowerPlatform/Dataverse/models/query_builder/index + /autoapi/PowerPlatform/Dataverse/models/record/index + /autoapi/PowerPlatform/Dataverse/models/relationship/index + /autoapi/PowerPlatform/Dataverse/models/table_info/index + /autoapi/PowerPlatform/Dataverse/models/upsert/index + + diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/models/labels/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/models/labels/index.rst new file mode 100644 index 00000000..f2df2a89 --- /dev/null +++ b/docs_local/autoapi/PowerPlatform/Dataverse/models/labels/index.rst @@ -0,0 +1,113 @@ +PowerPlatform.Dataverse.models.labels +===================================== + +.. py:module:: PowerPlatform.Dataverse.models.labels + +.. autoapi-nested-parse:: + + Label models for Dataverse metadata. + + + +Classes +------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.models.labels.LocalizedLabel + PowerPlatform.Dataverse.models.labels.Label + + +Module Contents +--------------- + +.. py:class:: LocalizedLabel + + Represents a localized label with a language code. + + :param label: The text of the label. + :type label: str + :param language_code: The language code (LCID), e.g., 1033 for English. + :type language_code: int + :param additional_properties: Optional dict of additional properties to include + in the Web API payload. These are merged last and can override default values. + :type additional_properties: Optional[Dict[str, Any]] + + + .. py:attribute:: label + :type: str + + + .. py:attribute:: language_code + :type: int + + + .. py:attribute:: additional_properties + :type: Optional[Dict[str, Any]] + :value: None + + + + .. py:method:: to_dict() -> Dict[str, Any] + + Convert to Web API JSON format. + + Example:: + + >>> label = LocalizedLabel(label="Account", language_code=1033) + >>> label.to_dict() + { + '@odata.type': 'Microsoft.Dynamics.CRM.LocalizedLabel', + 'Label': 'Account', + 'LanguageCode': 1033 + } + + + +.. py:class:: Label + + Represents a label that can have multiple localized versions. + + :param localized_labels: List of LocalizedLabel instances. + :type localized_labels: List[LocalizedLabel] + :param user_localized_label: Optional user-specific localized label. + :type user_localized_label: Optional[LocalizedLabel] + :param additional_properties: Optional dict of additional properties to include + in the Web API payload. These are merged last and can override default values. + :type additional_properties: Optional[Dict[str, Any]] + + + .. py:attribute:: localized_labels + :type: List[LocalizedLabel] + + + .. py:attribute:: user_localized_label + :type: Optional[LocalizedLabel] + :value: None + + + + .. py:attribute:: additional_properties + :type: Optional[Dict[str, Any]] + :value: None + + + + .. py:method:: to_dict() -> Dict[str, Any] + + Convert to Web API JSON format. + + Example:: + + >>> label = Label(localized_labels=[LocalizedLabel("Account", 1033)]) + >>> label.to_dict() + { + '@odata.type': 'Microsoft.Dynamics.CRM.Label', + 'LocalizedLabels': [ + {'@odata.type': '...', 'Label': 'Account', 'LanguageCode': 1033} + ], + 'UserLocalizedLabel': {'@odata.type': '...', 'Label': 'Account', ...} + } + + + diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/models/protocol/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/models/protocol/index.rst new file mode 100644 index 00000000..03a4fc46 --- /dev/null +++ b/docs_local/autoapi/PowerPlatform/Dataverse/models/protocol/index.rst @@ -0,0 +1,94 @@ +PowerPlatform.Dataverse.models.protocol +======================================= + +.. py:module:: PowerPlatform.Dataverse.models.protocol + +.. autoapi-nested-parse:: + + DataverseModel structural Protocol for typed entity integration. + + + +Classes +------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.models.protocol.DataverseModel + + +Module Contents +--------------- + +.. py:class:: DataverseModel + + Bases: :py:obj:`Protocol` + + + Structural Protocol enabling typed entity instances to be passed to + ``records.create()`` and ``records.update()``. + + Implement this Protocol on any entity class (dataclass, Pydantic model, + hand-rolled) to enable it to be passed directly to CRUD operations without + specifying the table name or converting to dict manually. + + Required class variables: + + - ``__entity_logical_name__`` — Dataverse logical entity name (e.g. ``"account"``) + - ``__entity_set_name__`` — OData entity set name (e.g. ``"accounts"``) + + Required instance methods: + + - ``to_dict()`` — return record payload as ``dict`` + - ``from_dict(data)`` — classmethod to reconstruct from a response ``dict`` + + Example:: + + from dataclasses import dataclass + from PowerPlatform.Dataverse import DataverseModel + + @dataclass + class Account: + __entity_logical_name__ = "account" + __entity_set_name__ = "accounts" + name: str = "" + telephone1: str = "" + + def to_dict(self) -> dict: + return {"name": self.name, "telephone1": self.telephone1} + + @classmethod + def from_dict(cls, data: dict) -> "Account": + return cls( + name=data.get("name", ""), + telephone1=data.get("telephone1", ""), + ) + + # isinstance() works today — Protocol is runtime_checkable: + assert isinstance(Account(), DataverseModel) + + # Type your own helpers against the Protocol now: + def save(entity: DataverseModel) -> None: + data = entity.to_dict() + client.records.create(entity.__entity_logical_name__, data) + + .. note:: + + Direct dispatch (``client.records.create(entity)`` without a table name + or dict) is not yet supported and will be added in a future release. + + + .. py:method:: to_dict() -> dict + + Return the record payload as a plain dictionary. + + + + .. py:method:: from_dict(data: dict) -> DataverseModel + :classmethod: + + + Reconstruct an instance from a response dictionary. + + + diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/models/query_builder/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/models/query_builder/index.rst new file mode 100644 index 00000000..9e8a9583 --- /dev/null +++ b/docs_local/autoapi/PowerPlatform/Dataverse/models/query_builder/index.rst @@ -0,0 +1,332 @@ +PowerPlatform.Dataverse.models.query_builder +============================================ + +.. py:module:: PowerPlatform.Dataverse.models.query_builder + +.. autoapi-nested-parse:: + + Fluent query builder for constructing OData queries. + + Provides a type-safe, discoverable interface for building complex queries + against Dataverse tables with method chaining. + + Example:: + + # Via client (recommended) -- flat iteration over records + from PowerPlatform.Dataverse.models import col + + for record in (client.query.builder("account") + .select("name", "revenue") + .where(col("statecode") == 0) + .where(col("revenue") > 1_000_000) + .order_by("revenue", descending=True) + .top(100) + .execute()): + print(record["name"]) + + # With composable expression tree + from PowerPlatform.Dataverse.models import col, raw + + for record in (client.query.builder("account") + .select("name", "revenue") + .where((col("statecode") == 0) | (col("statecode") == 1)) + .where(col("revenue") > 100000) + .top(100) + .execute()): + print(record["name"]) + + # Lazy paged iteration (one QueryResult per HTTP page) + for page in (client.query.builder("account") + .select("name") + .execute_pages()): + process_batch(page) + + # Get results as a pandas DataFrame + df = (client.query.builder("account") + .select("name", "telephone1") + .where(col("statecode") == 0) + .top(100) + .execute() + .to_dataframe()) + + + +Classes +------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.models.query_builder.QueryParams + PowerPlatform.Dataverse.models.query_builder.ExpandOption + PowerPlatform.Dataverse.models.query_builder.QueryBuilder + + +Module Contents +--------------- + +.. py:class:: QueryParams + + Bases: :py:obj:`TypedDict` + + + Typed dictionary returned by ``QueryBuilder.build()``. + + Provides IDE autocomplete when passing build results to + ``client.records.list()`` manually. + + Initialize self. See help(type(self)) for accurate signature. + + + .. py:attribute:: table + :type: str + + + .. py:attribute:: select + :type: List[str] + + + .. py:attribute:: filter + :type: str + + + .. py:attribute:: orderby + :type: List[str] + + + .. py:attribute:: expand + :type: List[str] + + + .. py:attribute:: top + :type: int + + + .. py:attribute:: page_size + :type: int + + + .. py:attribute:: count + :type: bool + + + .. py:attribute:: include_annotations + :type: str + + +.. py:class:: ExpandOption(relation: str) + + Structured options for an ``$expand`` navigation property. + + Allows specifying nested ``$select``, ``$filter``, ``$orderby``, and + ``$top`` options for a single navigation property expansion, following + the OData ``$expand`` syntax. + + :param relation: Navigation property name (case-sensitive). + :type relation: str + + Example:: + + # Expand Account_Tasks with nested options + opt = (ExpandOption("Account_Tasks") + .select("subject", "createdon") + .filter("contains(subject,'Task')") + .order_by("createdon", descending=True) + .top(5)) + + query = (client.query.builder("account") + .select("name") + .expand(opt) + .execute()) + + + .. py:attribute:: relation + + + .. py:method:: select(*columns: str) -> ExpandOption + + Select specific columns from the expanded entity. + + :param columns: Column names to select. + :return: Self for method chaining. + + + + .. py:method:: filter(filter_str: str) -> ExpandOption + + Filter the expanded collection. + + :param filter_str: OData ``$filter`` expression. + :return: Self for method chaining. + + + + .. py:method:: order_by(column: str, descending: bool = False) -> ExpandOption + + Sort the expanded collection. + + :param column: Column name to sort by. + :param descending: Sort descending if ``True``. + :return: Self for method chaining. + + + + .. py:method:: top(count: int) -> ExpandOption + + Limit expanded results. + + :param count: Maximum number of expanded records. + :return: Self for method chaining. + + + + .. py:method:: to_odata() -> str + + Compile to OData ``$expand`` syntax. + + :return: OData expand string like ``"Nav($select=col1,col2;$filter=...)"`` + :rtype: str + + + +.. py:class:: QueryBuilder(table: str) + + Bases: :py:obj:`_QueryBuilderBase` + + + Fluent interface for building and executing OData queries against a sync client. + + Provides method chaining for constructing complex queries with + composable filter expressions. Can be used standalone (via ``build()``) + or bound to a client (via :meth:`execute`). + + :param table: Table schema name to query. + :type table: str + :raises ValueError: If ``table`` is empty. + + .. rubric:: Example + + Standalone query construction:: + + from PowerPlatform.Dataverse.models import col + + query = (QueryBuilder("account") + .select("name") + .where(col("statecode") == 0) + .top(10)) + params = query.build() + # {"table": "account", "select": ["name"], + # "filter": "statecode eq 0", "top": 10} + + + .. py:method:: execute(*, by_page=_BY_PAGE_UNSET) -> Union[PowerPlatform.Dataverse.models.record.QueryResult, Iterator[PowerPlatform.Dataverse.models.record.QueryResult]] + + Execute the query and return results. + + Returns a :class:`~PowerPlatform.Dataverse.models.record.QueryResult` + with all pages collected. Use :meth:`execute_pages` for lazy per-page + iteration. + + This method is only available when the QueryBuilder was created + via ``client.query.builder(table)``. Standalone ``QueryBuilder`` + instances should use ``build()`` to get parameters and pass them + to ``client.records.list()`` manually. + + At least one of ``select()``, ``where()``, or ``top()`` must be + called before ``execute()``; otherwise a :class:`ValueError` is + raised to prevent accidental full-table scans. + + .. deprecated:: + The ``by_page`` parameter is deprecated. Use :meth:`execute_pages` + for lazy per-page iteration, or plain ``execute()`` (no flag) for + the default eager result. + + :return: :class:`~PowerPlatform.Dataverse.models.record.QueryResult` + with all pages collected (default), or page iterator (deprecated + ``by_page=True``). + :rtype: QueryResult or Iterator[QueryResult] + :raises ValueError: If no ``select``, ``where``, or ``top`` + constraint has been set. + :raises RuntimeError: If the query was not created via + ``client.query.builder()``. + + Example:: + + from PowerPlatform.Dataverse.models import col + + for record in (client.query.builder("account") + .select("name") + .where(col("statecode") == 0) + .execute()): + print(record["name"]) + + + + .. py:method:: execute_pages() -> Iterator[PowerPlatform.Dataverse.models.record.QueryResult] + + Lazily yield one :class:`~PowerPlatform.Dataverse.models.record.QueryResult` + per HTTP page. + + Each iteration triggers a network request via ``@odata.nextLink``. + One-shot — do not iterate more than once. + + At least one of ``select()``, ``where()``, or ``top()`` must be + called before ``execute_pages()``; otherwise a :class:`ValueError` is + raised to prevent accidental full-table scans. + + :return: Iterator of per-page :class:`~PowerPlatform.Dataverse.models.record.QueryResult`. + :rtype: Iterator[QueryResult] + :raises ValueError: If no ``select``, ``where``, or ``top`` + constraint has been set. + :raises RuntimeError: If the query was not created via + ``client.query.builder()``. + + Example:: + + from PowerPlatform.Dataverse.models import col + + for page in (client.query.builder("account") + .select("name") + .where(col("statecode") == 0) + .execute_pages()): + process(page.to_dataframe()) + + + + .. py:method:: to_dataframe() -> pandas.DataFrame + + Execute the query and return results as a pandas DataFrame. + + .. deprecated:: + Use ``QueryBuilder.execute().to_dataframe()`` instead. + ``QueryBuilder.to_dataframe()`` will be removed in a future release. + + All pages are consolidated into a single DataFrame. + + This method is only available when the QueryBuilder was created + via ``client.query.builder(table)``. + + At least one of ``select()``, ``where()``, or ``top()`` must be + called before ``to_dataframe()``; otherwise a :class:`ValueError` + is raised to prevent accidental full-table scans. + + :return: DataFrame containing all matching records. Returns an empty + DataFrame when no records match. + :rtype: ~pandas.DataFrame + :raises ValueError: If no ``select``, ``where``, or ``top`` + constraint has been set. + :raises RuntimeError: If the query was not created via + ``client.query.builder()``. + + Example:: + + from PowerPlatform.Dataverse.models import col + + df = (client.query.builder("account") + .select("name", "telephone1") + .where(col("statecode") == 0) + .top(100) + .execute() + .to_dataframe()) + + + diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/models/record/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/models/record/index.rst new file mode 100644 index 00000000..d92274ed --- /dev/null +++ b/docs_local/autoapi/PowerPlatform/Dataverse/models/record/index.rst @@ -0,0 +1,152 @@ +PowerPlatform.Dataverse.models.record +===================================== + +.. py:module:: PowerPlatform.Dataverse.models.record + +.. autoapi-nested-parse:: + + Record data model for Dataverse entities. + + + +Classes +------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.models.record.Record + PowerPlatform.Dataverse.models.record.QueryResult + + +Module Contents +--------------- + +.. py:class:: Record + + Strongly-typed Dataverse record with dict-like backward compatibility. + + Wraps raw OData response data into a structured object while preserving + ``result["key"]`` access patterns for existing code. + + :param id: Record GUID. Empty string if not extracted (e.g. paginated + results, SQL queries). + :type id: :class:`str` + :param table: Table schema name used in the request. + :type table: :class:`str` + :param data: Record field data as key-value pairs. + :type data: :class:`dict` + :param etag: ETag for optimistic concurrency, extracted from + ``@odata.etag`` in the API response. + :type etag: :class:`str` or None + + Example:: + + record = client.records.get("account", account_id, select=["name"]) + print(record.id) # structured access + print(record["name"]) # dict-like access (backward compat) + + + .. py:attribute:: id + :type: str + :value: '' + + + + .. py:attribute:: table + :type: str + :value: '' + + + + .. py:attribute:: data + :type: Dict[str, Any] + + + .. py:attribute:: etag + :type: Optional[str] + :value: None + + + + .. py:method:: get(key: str, default: Any = None) -> Any + + Return value for *key*, or *default* if not present. + + + + .. py:method:: keys() -> KeysView[str] + + Return data keys. + + + + .. py:method:: values() -> ValuesView[Any] + + Return data values. + + + + .. py:method:: items() -> ItemsView[str, Any] + + Return data items. + + + + .. py:method:: from_api_response(table: str, response_data: Dict[str, Any], *, record_id: str = '') -> Record + :classmethod: + + + Create a :class:`Record` from a raw OData API response. + + Strips ``@odata.*`` annotation keys from the data and extracts the + ``@odata.etag`` value if present. + + :param table: Table schema name. + :type table: :class:`str` + :param response_data: Raw JSON dict from the OData response. + :type response_data: :class:`dict` + :param record_id: Known record GUID. Pass explicitly when available + (e.g. single-record get). Defaults to empty string. + :type record_id: :class:`str` + :rtype: :class:`Record` + + + + .. py:method:: to_dict() -> Dict[str, Any] + + Return a plain dict copy of the record data (excludes metadata). + + + +.. py:class:: QueryResult(records: List[Record]) + + Iterable wrapper around a list of :class:`Record` objects. + + Returned by :meth:`~PowerPlatform.Dataverse.models.query_builder.QueryBuilder.execute` + (flat mode) and :meth:`~PowerPlatform.Dataverse.operations.records.RecordOperations.list`. + + Backward-compatible: ``for r in result`` continues to work without change. + + :param records: Collected records from all pages. + :type records: list[:class:`Record`] + + + .. py:attribute:: records + :type: List[Record] + + + .. py:method:: first() -> Optional[Record] + + Return the first record, or ``None`` if the result is empty. + + + + .. py:method:: to_dataframe() -> Any + + Return all records as a pandas DataFrame. + + :raises ImportError: If pandas is not installed. + :rtype: ~pandas.DataFrame + + + diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/models/relationship/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/models/relationship/index.rst new file mode 100644 index 00000000..06160b7a --- /dev/null +++ b/docs_local/autoapi/PowerPlatform/Dataverse/models/relationship/index.rst @@ -0,0 +1,471 @@ +PowerPlatform.Dataverse.models.relationship +=========================================== + +.. py:module:: PowerPlatform.Dataverse.models.relationship + +.. autoapi-nested-parse:: + + Relationship models for Dataverse (input and output). + + + +Classes +------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.models.relationship.CascadeConfiguration + PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata + PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata + PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata + PowerPlatform.Dataverse.models.relationship.RelationshipInfo + + +Module Contents +--------------- + +.. py:class:: CascadeConfiguration + + Defines cascade behavior for relationship operations. + + :param assign: Cascade behavior for assign operations. + :type assign: str + :param delete: Cascade behavior for delete operations. + :type delete: str + :param merge: Cascade behavior for merge operations. + :type merge: str + :param reparent: Cascade behavior for reparent operations. + :type reparent: str + :param share: Cascade behavior for share operations. + :type share: str + :param unshare: Cascade behavior for unshare operations. + :type unshare: str + :param additional_properties: Optional dict of additional properties to include + in the Web API payload (e.g., "Archive", "RollupView"). These are merged + last and can override default values. + :type additional_properties: Optional[Dict[str, Any]] + + Valid values for each parameter: + - "Cascade": Perform the operation on all related records + - "NoCascade": Do not perform the operation on related records + - "RemoveLink": Remove the relationship link but keep the records + - "Restrict": Prevent the operation if related records exist + + + .. py:attribute:: assign + :type: str + :value: 'NoCascade' + + + + .. py:attribute:: delete + :type: str + :value: 'RemoveLink' + + + + .. py:attribute:: merge + :type: str + :value: 'NoCascade' + + + + .. py:attribute:: reparent + :type: str + :value: 'NoCascade' + + + + .. py:attribute:: share + :type: str + :value: 'NoCascade' + + + + .. py:attribute:: unshare + :type: str + :value: 'NoCascade' + + + + .. py:attribute:: additional_properties + :type: Optional[Dict[str, Any]] + :value: None + + + + .. py:method:: to_dict() -> Dict[str, Any] + + Convert to Web API JSON format. + + Example:: + + >>> config = CascadeConfiguration(delete="Cascade", assign="NoCascade") + >>> config.to_dict() + { + 'Assign': 'NoCascade', + 'Delete': 'Cascade', + 'Merge': 'NoCascade', + 'Reparent': 'NoCascade', + 'Share': 'NoCascade', + 'Unshare': 'NoCascade' + } + + + +.. py:class:: LookupAttributeMetadata + + Metadata for a lookup attribute. + + :param schema_name: Schema name for the attribute (e.g., "new_AccountId"). + :type schema_name: str + :param display_name: Display name for the attribute. + :type display_name: Label + :param description: Optional description of the attribute. + :type description: Optional[Label] + :param required_level: Requirement level for the attribute. + :type required_level: str + :param additional_properties: Optional dict of additional properties to include + in the Web API payload. Useful for setting properties like "Targets" (to + specify which entity types the lookup can reference), "LogicalName", + "IsSecured", "IsValidForAdvancedFind", etc. These are merged last and + can override default values. + :type additional_properties: Optional[Dict[str, Any]] + + Valid required_level values: + - "None": The attribute is optional + - "Recommended": The attribute is recommended + - "ApplicationRequired": The attribute is required + + + .. py:attribute:: schema_name + :type: str + + + .. py:attribute:: display_name + :type: PowerPlatform.Dataverse.models.labels.Label + + + .. py:attribute:: description + :type: Optional[PowerPlatform.Dataverse.models.labels.Label] + :value: None + + + + .. py:attribute:: required_level + :type: str + :value: 'None' + + + + .. py:attribute:: additional_properties + :type: Optional[Dict[str, Any]] + :value: None + + + + .. py:method:: to_dict() -> Dict[str, Any] + + Convert to Web API JSON format. + + Example:: + + >>> lookup = LookupAttributeMetadata( + ... schema_name="new_AccountId", + ... display_name=Label([LocalizedLabel("Account", 1033)]) + ... ) + >>> lookup.to_dict() + { + '@odata.type': 'Microsoft.Dynamics.CRM.LookupAttributeMetadata', + 'SchemaName': 'new_AccountId', + 'AttributeType': 'Lookup', + 'AttributeTypeName': {'Value': 'LookupType'}, + 'DisplayName': {...}, + 'RequiredLevel': {'Value': 'None', 'CanBeChanged': True, ...} + } + + + +.. py:class:: OneToManyRelationshipMetadata + + Metadata for a one-to-many entity relationship. + + :param schema_name: Schema name for the relationship (e.g., "new_Account_Orders"). + :type schema_name: str + :param referenced_entity: Logical name of the referenced (parent) entity. + :type referenced_entity: str + :param referencing_entity: Logical name of the referencing (child) entity. + :type referencing_entity: str + :param referenced_attribute: Attribute on the referenced entity (typically the primary key). + :type referenced_attribute: str + :param cascade_configuration: Cascade behavior configuration. + :type cascade_configuration: CascadeConfiguration + :param referencing_attribute: Optional name for the referencing attribute (usually auto-generated). + :type referencing_attribute: Optional[str] + :param additional_properties: Optional dict of additional properties to include + in the Web API payload. Useful for setting inherited properties like + "IsValidForAdvancedFind", "IsCustomizable", "SecurityTypes", etc. + These are merged last and can override default values. + :type additional_properties: Optional[Dict[str, Any]] + + + .. py:attribute:: schema_name + :type: str + + + .. py:attribute:: referenced_entity + :type: str + + + .. py:attribute:: referencing_entity + :type: str + + + .. py:attribute:: referenced_attribute + :type: str + + + .. py:attribute:: cascade_configuration + :type: CascadeConfiguration + + + .. py:attribute:: referencing_attribute + :type: Optional[str] + :value: None + + + + .. py:attribute:: additional_properties + :type: Optional[Dict[str, Any]] + :value: None + + + + .. py:method:: to_dict() -> Dict[str, Any] + + Convert to Web API JSON format. + + Example:: + + >>> rel = OneToManyRelationshipMetadata( + ... schema_name="new_account_orders", + ... referenced_entity="account", + ... referencing_entity="new_order", + ... referenced_attribute="accountid" + ... ) + >>> rel.to_dict() + { + '@odata.type': 'Microsoft.Dynamics.CRM.OneToManyRelationshipMetadata', + 'SchemaName': 'new_account_orders', + 'ReferencedEntity': 'account', + 'ReferencingEntity': 'new_order', + 'ReferencedAttribute': 'accountid', + 'CascadeConfiguration': {...} + } + + + +.. py:class:: ManyToManyRelationshipMetadata + + Metadata for a many-to-many entity relationship. + + :param schema_name: Schema name for the relationship. + :type schema_name: str + :param entity1_logical_name: Logical name of the first entity. + :type entity1_logical_name: str + :param entity2_logical_name: Logical name of the second entity. + :type entity2_logical_name: str + :param intersect_entity_name: Name for the intersect table (defaults to schema_name if not provided). + :type intersect_entity_name: Optional[str] + :param additional_properties: Optional dict of additional properties to include + in the Web API payload. Useful for setting inherited properties like + "IsValidForAdvancedFind", "IsCustomizable", "SecurityTypes", or direct + properties like "Entity1NavigationPropertyName". These are merged last + and can override default values. + :type additional_properties: Optional[Dict[str, Any]] + + + .. py:attribute:: schema_name + :type: str + + + .. py:attribute:: entity1_logical_name + :type: str + + + .. py:attribute:: entity2_logical_name + :type: str + + + .. py:attribute:: intersect_entity_name + :type: Optional[str] + :value: None + + + + .. py:attribute:: additional_properties + :type: Optional[Dict[str, Any]] + :value: None + + + + .. py:method:: to_dict() -> Dict[str, Any] + + Convert to Web API JSON format. + + Example:: + + >>> rel = ManyToManyRelationshipMetadata( + ... schema_name="new_account_contact", + ... entity1_logical_name="account", + ... entity2_logical_name="contact" + ... ) + >>> rel.to_dict() + { + '@odata.type': 'Microsoft.Dynamics.CRM.ManyToManyRelationshipMetadata', + 'SchemaName': 'new_account_contact', + 'Entity1LogicalName': 'account', + 'Entity2LogicalName': 'contact', + 'IntersectEntityName': 'new_account_contact' + } + + + +.. py:class:: RelationshipInfo + + Typed return model for relationship metadata. + + Returned by :meth:`~PowerPlatform.Dataverse.operations.tables.TableOperations.create_one_to_many_relationship`, + :meth:`~PowerPlatform.Dataverse.operations.tables.TableOperations.create_many_to_many_relationship`, + :meth:`~PowerPlatform.Dataverse.operations.tables.TableOperations.get_relationship`, and + :meth:`~PowerPlatform.Dataverse.operations.tables.TableOperations.create_lookup_field`. + + :param relationship_id: Relationship metadata GUID. + :type relationship_id: :class:`str` or None + :param relationship_schema_name: Relationship schema name. + :type relationship_schema_name: :class:`str` + :param relationship_type: Either ``"one_to_many"`` or ``"many_to_many"``. + :type relationship_type: :class:`str` + :param lookup_schema_name: Lookup field schema name (one-to-many only). + :type lookup_schema_name: :class:`str` or None + :param referenced_entity: Parent entity logical name (one-to-many only). + :type referenced_entity: :class:`str` or None + :param referencing_entity: Child entity logical name (one-to-many only). + :type referencing_entity: :class:`str` or None + :param entity1_logical_name: First entity logical name (many-to-many only). + :type entity1_logical_name: :class:`str` or None + :param entity2_logical_name: Second entity logical name (many-to-many only). + :type entity2_logical_name: :class:`str` or None + + Example:: + + result = client.tables.create_one_to_many_relationship(lookup, relationship) + print(result.relationship_schema_name) + print(result.lookup_schema_name) + + + .. py:attribute:: relationship_id + :type: Optional[str] + :value: None + + + + .. py:attribute:: relationship_schema_name + :type: str + :value: '' + + + + .. py:attribute:: relationship_type + :type: str + :value: '' + + + + .. py:attribute:: lookup_schema_name + :type: Optional[str] + :value: None + + + + .. py:attribute:: referenced_entity + :type: Optional[str] + :value: None + + + + .. py:attribute:: referencing_entity + :type: Optional[str] + :value: None + + + + .. py:attribute:: entity1_logical_name + :type: Optional[str] + :value: None + + + + .. py:attribute:: entity2_logical_name + :type: Optional[str] + :value: None + + + + .. py:method:: from_one_to_many(*, relationship_id: Optional[str], relationship_schema_name: str, lookup_schema_name: str, referenced_entity: str, referencing_entity: str) -> RelationshipInfo + :classmethod: + + + Create from a one-to-many relationship result. + + :param relationship_id: Relationship metadata GUID. + :type relationship_id: :class:`str` or None + :param relationship_schema_name: Relationship schema name. + :type relationship_schema_name: :class:`str` + :param lookup_schema_name: Lookup field schema name. + :type lookup_schema_name: :class:`str` + :param referenced_entity: Parent entity logical name. + :type referenced_entity: :class:`str` + :param referencing_entity: Child entity logical name. + :type referencing_entity: :class:`str` + :rtype: :class:`RelationshipInfo` + + + + .. py:method:: from_many_to_many(*, relationship_id: Optional[str], relationship_schema_name: str, entity1_logical_name: str, entity2_logical_name: str) -> RelationshipInfo + :classmethod: + + + Create from a many-to-many relationship result. + + :param relationship_id: Relationship metadata GUID. + :type relationship_id: :class:`str` or None + :param relationship_schema_name: Relationship schema name. + :type relationship_schema_name: :class:`str` + :param entity1_logical_name: First entity logical name. + :type entity1_logical_name: :class:`str` + :param entity2_logical_name: Second entity logical name. + :type entity2_logical_name: :class:`str` + :rtype: :class:`RelationshipInfo` + + + + .. py:method:: from_api_response(response_data: Dict[str, Any]) -> RelationshipInfo + :classmethod: + + + Create from a raw Dataverse Web API response. + + Detects one-to-many vs many-to-many from the ``@odata.type`` field + in the response and maps PascalCase keys to snake_case attributes. + Dataverse only supports these two relationship types; an unrecognized + ``@odata.type`` raises :class:`ValueError`. + + :param response_data: Raw relationship metadata from the Web API. + :type response_data: :class:`dict` + :rtype: :class:`RelationshipInfo` + :raises ValueError: If the ``@odata.type`` is not a recognized + relationship type. + + + diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/models/table_info/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/models/table_info/index.rst new file mode 100644 index 00000000..fd73d890 --- /dev/null +++ b/docs_local/autoapi/PowerPlatform/Dataverse/models/table_info/index.rst @@ -0,0 +1,305 @@ +PowerPlatform.Dataverse.models.table_info +========================================= + +.. py:module:: PowerPlatform.Dataverse.models.table_info + +.. autoapi-nested-parse:: + + Table and column metadata models for Dataverse. + + + +Classes +------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.models.table_info.ColumnInfo + PowerPlatform.Dataverse.models.table_info.TableInfo + PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo + + +Module Contents +--------------- + +.. py:class:: ColumnInfo + + Column metadata from a Dataverse table definition. + + :param schema_name: Column schema name (e.g. ``"new_Price"``). + :type schema_name: :class:`str` + :param logical_name: Column logical name (lowercase). + :type logical_name: :class:`str` + :param type: Column type string (e.g. ``"String"``, ``"Integer"``). + :type type: :class:`str` + :param is_primary: Whether this is the primary name column. + :type is_primary: :class:`bool` + :param is_required: Whether the column is required. + :type is_required: :class:`bool` + :param max_length: Maximum length for string columns. + :type max_length: :class:`int` or None + :param display_name: Human-readable display name. + :type display_name: :class:`str` or None + :param description: Column description. + :type description: :class:`str` or None + + + .. py:attribute:: schema_name + :type: str + :value: '' + + + + .. py:attribute:: logical_name + :type: str + :value: '' + + + + .. py:attribute:: type + :type: str + :value: '' + + + + .. py:attribute:: is_primary + :type: bool + :value: False + + + + .. py:attribute:: is_required + :type: bool + :value: False + + + + .. py:attribute:: max_length + :type: Optional[int] + :value: None + + + + .. py:attribute:: display_name + :type: Optional[str] + :value: None + + + + .. py:attribute:: description + :type: Optional[str] + :value: None + + + + .. py:method:: from_api_response(response_data: Dict[str, Any]) -> ColumnInfo + :classmethod: + + + Create from a raw Dataverse ``AttributeMetadata`` API response. + + :param response_data: Raw attribute metadata dict (PascalCase keys). + :type response_data: :class:`dict` + :rtype: :class:`ColumnInfo` + + + +.. py:class:: TableInfo + + Table metadata with dict-like backward compatibility. + + Supports both new attribute access (``info.schema_name``) and legacy + dict-key access (``info["table_schema_name"]``) for backward + compatibility with code written against the raw dict API. + + :param schema_name: Table schema name (e.g. ``"Account"``). + :type schema_name: :class:`str` + :param logical_name: Table logical name (lowercase). + :type logical_name: :class:`str` + :param entity_set_name: OData entity set name. + :type entity_set_name: :class:`str` + :param metadata_id: Metadata GUID. + :type metadata_id: :class:`str` + :param display_name: Human-readable display name. + :type display_name: :class:`str` or None + :param description: Table description. + :type description: :class:`str` or None + :param columns: Column metadata (when retrieved). + :type columns: list[ColumnInfo] or None + :param columns_created: Column schema names created with the table. + :type columns_created: list[str] or None + + Example:: + + info = client.tables.create("new_Product", {"new_Price": "decimal"}) + print(info.schema_name) # new attribute access + print(info["table_schema_name"]) # legacy dict-key access + + + .. py:attribute:: schema_name + :type: str + :value: '' + + + + .. py:attribute:: logical_name + :type: str + :value: '' + + + + .. py:attribute:: entity_set_name + :type: str + :value: '' + + + + .. py:attribute:: metadata_id + :type: str + :value: '' + + + + .. py:attribute:: primary_name_attribute + :type: Optional[str] + :value: None + + + + .. py:attribute:: primary_id_attribute + :type: Optional[str] + :value: None + + + + .. py:attribute:: display_name + :type: Optional[str] + :value: None + + + + .. py:attribute:: description + :type: Optional[str] + :value: None + + + + .. py:attribute:: columns + :type: Optional[List[ColumnInfo]] + :value: None + + + + .. py:attribute:: columns_created + :type: Optional[List[str]] + :value: None + + + + .. py:method:: get(key: str, default: Any = None) -> Any + + Return value for *key*, or *default* if not present. + + + + .. py:method:: keys() -> KeysView[str] + + Return legacy dict keys. + + + + .. py:method:: values() -> List[Any] + + Return values corresponding to legacy dict keys. + + + + .. py:method:: items() -> List[tuple] + + Return (legacy_key, value) pairs. + + + + .. py:method:: from_dict(data: Dict[str, Any]) -> TableInfo + :classmethod: + + + Create from an SDK internal dict (snake_case keys). + + This handles the dict format returned by ``_create_table`` and + ``_get_table_info`` in the OData layer. + + :param data: Dictionary with SDK snake_case keys. + :type data: :class:`dict` + :rtype: :class:`TableInfo` + + + + .. py:method:: from_api_response(response_data: Dict[str, Any]) -> TableInfo + :classmethod: + + + Create from a raw Dataverse ``EntityDefinition`` API response. + + :param response_data: Raw entity metadata dict (PascalCase keys). + :type response_data: :class:`dict` + :rtype: :class:`TableInfo` + + + + .. py:method:: to_dict() -> Dict[str, Any] + + Return a dict with legacy keys for backward compatibility. + + + +.. py:class:: AlternateKeyInfo + + Alternate key metadata for a Dataverse table. + + :param metadata_id: Key metadata GUID. + :type metadata_id: :class:`str` + :param schema_name: Key schema name. + :type schema_name: :class:`str` + :param key_attributes: List of column logical names that compose the key. + :type key_attributes: list[str] + :param status: Index creation status (``"Active"``, ``"Pending"``, ``"InProgress"``, ``"Failed"``). + :type status: :class:`str` + + + .. py:attribute:: metadata_id + :type: str + :value: '' + + + + .. py:attribute:: schema_name + :type: str + :value: '' + + + + .. py:attribute:: key_attributes + :type: List[str] + :value: [] + + + + .. py:attribute:: status + :type: str + :value: '' + + + + .. py:method:: from_api_response(response_data: Dict[str, Any]) -> AlternateKeyInfo + :classmethod: + + + Create from raw EntityKeyMetadata API response. + + :param response_data: Raw key metadata dictionary from the Web API. + :type response_data: :class:`dict` + :rtype: :class:`AlternateKeyInfo` + + + diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/models/upsert/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/models/upsert/index.rst new file mode 100644 index 00000000..b6568676 --- /dev/null +++ b/docs_local/autoapi/PowerPlatform/Dataverse/models/upsert/index.rst @@ -0,0 +1,54 @@ +PowerPlatform.Dataverse.models.upsert +===================================== + +.. py:module:: PowerPlatform.Dataverse.models.upsert + +.. autoapi-nested-parse:: + + Upsert data models for the Dataverse SDK. + + + +Classes +------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.models.upsert.UpsertItem + + +Module Contents +--------------- + +.. py:class:: UpsertItem + + Represents a single upsert operation targeting a record by its alternate key. + + Used with :meth:`~PowerPlatform.Dataverse.operations.records.RecordOperations.upsert` + to upsert one or more records identified by alternate keys rather than primary GUIDs. + + :param alternate_key: Dictionary mapping alternate key attribute names to their values. + String values are automatically quoted and escaped in the OData URL. Integer and + other non-string values are included without quotes. + :type alternate_key: dict[str, Any] + :param record: Dictionary of attribute names to values for the record payload. + Keys are automatically lowercased. Picklist labels are resolved to integer option + values when a matching option set is found. + :type record: dict[str, Any] + + Example:: + + item = UpsertItem( + alternate_key={"accountnumber": "ACC-001", "address1_postalcode": "98052"}, + record={"name": "Contoso Ltd", "telephone1": "555-0100"}, + ) + + + .. py:attribute:: alternate_key + :type: Dict[str, Any] + + + .. py:attribute:: record + :type: Dict[str, Any] + + diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/operations/batch/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/operations/batch/index.rst new file mode 100644 index 00000000..160530b7 --- /dev/null +++ b/docs_local/autoapi/PowerPlatform/Dataverse/operations/batch/index.rst @@ -0,0 +1,741 @@ +PowerPlatform.Dataverse.operations.batch +======================================== + +.. py:module:: PowerPlatform.Dataverse.operations.batch + +.. autoapi-nested-parse:: + + Batch operation namespaces for the Dataverse SDK. + + + +Classes +------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.operations.batch.ChangeSetRecordOperations + PowerPlatform.Dataverse.operations.batch.ChangeSet + PowerPlatform.Dataverse.operations.batch.BatchRecordOperations + PowerPlatform.Dataverse.operations.batch.BatchTableOperations + PowerPlatform.Dataverse.operations.batch.BatchQueryOperations + PowerPlatform.Dataverse.operations.batch.BatchDataFrameOperations + PowerPlatform.Dataverse.operations.batch.BatchRequest + PowerPlatform.Dataverse.operations.batch.BatchOperations + + +Module Contents +--------------- + +.. py:class:: ChangeSetRecordOperations(cs_internal: PowerPlatform.Dataverse.data._batch._ChangeSet) + + Record write operations available inside a :class:`ChangeSet`. + + Mirrors ``client.records`` but restricted to single-record forms (no bulk + create/update/delete). Only write operations are allowed — GET is not + permitted inside a changeset. + + Do not instantiate directly; use ``ChangeSet.records``. + + + .. py:method:: create(table: str, data: Dict[str, Any]) -> str + + Add a single-record create to this changeset. + + :param table: Table schema name (e.g. ``"account"``). + :type table: :class:`str` + :param data: Column values for the new record. + :type data: dict[str, typing.Any] + :returns: A content-ID reference string (e.g. ``"$1"``) usable in + subsequent operations within this changeset as a URI reference + in ``@odata.bind`` fields or as ``record_id`` in + :meth:`update` / :meth:`delete`. + :rtype: :class:`str` + + Example:: + + with batch.changeset() as cs: + lead_ref = cs.records.create("lead", {"firstname": "Ada"}) + cs.records.create("account", { + "name": "Babbage", + "originatingleadid@odata.bind": lead_ref, + }) + + + + .. py:method:: update(table: str, record_id: str, changes: Dict[str, Any]) -> None + + Add a single-record update to this changeset. + + :param table: Table schema name. Ignored when ``record_id`` is a + content-ID reference. + :type table: :class:`str` + :param record_id: GUID or a content-ID reference (e.g. ``"$1"``) + returned by a prior :meth:`create` in this changeset. + :type record_id: :class:`str` + :param changes: Column values to update. + :type changes: dict[str, typing.Any] + + + + .. py:method:: delete(table: str, record_id: str) -> None + + Add a single-record delete to this changeset. + + :param table: Table schema name. Ignored when ``record_id`` is a + content-ID reference. + :type table: :class:`str` + :param record_id: GUID or a content-ID reference (e.g. ``"$1"``). + :type record_id: :class:`str` + + + +.. py:class:: ChangeSet(internal: PowerPlatform.Dataverse.data._batch._ChangeSet) + + A transactional group of single-record write operations. + + All operations succeed or are rolled back together. Use as a context + manager or call ``records`` to add operations directly. + + Do not instantiate directly; use :meth:`BatchRequest.changeset`. + + Example:: + + with batch.changeset() as cs: + ref = cs.records.create("contact", {"firstname": "Alice"}) + cs.records.update("account", account_id, { + "primarycontactid@odata.bind": ref + }) + + + .. py:attribute:: records + + +.. py:class:: BatchRecordOperations(batch: _BatchContext) + + Record operations on a :class:`BatchRequest`. + + Mirrors ``client.records``: same method names, same signatures. + All methods return ``None``; results are available via + :class:`~PowerPlatform.Dataverse.models.batch.BatchResult` after + :meth:`BatchRequest.execute`. + + GA methods: :meth:`retrieve` (single record) and :meth:`list` (multi-record, + single page). :meth:`get` is deprecated — use :meth:`retrieve` instead. + + Do not instantiate directly; use ``batch.records``. + + + .. py:method:: create(table: str, data: Union[Dict[str, Any], List[Dict[str, Any]]]) -> None + + Add a create operation to the batch. + + A single dict creates one record (POST entity_set). + A list of dicts creates all records via the ``CreateMultiple`` action + (one batch item). + + :param table: Table schema name (e.g. ``"account"``). + :type table: :class:`str` + :param data: Single record dict or list of record dicts. + :type data: dict or list[dict] + + + + .. py:method:: update(table: str, ids: Union[str, List[str]], changes: Union[Dict[str, Any], List[Dict[str, Any]]]) -> None + + Add an update operation to the batch. + + - **Single** ``(table, "guid", {...})`` -> one PATCH request. + - **Broadcast** ``(table, [id1, id2], {...})`` -> one ``UpdateMultiple`` POST. + - **Paired** ``(table, [id1, id2], [{...}, {...}])`` -> one ``UpdateMultiple`` POST. + + :param table: Table schema name. + :type table: :class:`str` + :param ids: Single GUID or list of GUIDs. + :type ids: str or list[str] + :param changes: Single dict (single/broadcast) or list of dicts (paired). + :type changes: dict or list[dict] + + + + .. py:method:: delete(table: str, ids: Union[str, List[str]], *, use_bulk_delete: bool = True) -> None + + Add a delete operation to the batch. + + - **Single** ``(table, "guid")`` -> one DELETE request. + - **List + use_bulk_delete=True** (default) -> one ``BulkDelete`` POST. + The async job ID will be available in ``BatchItemResponse.data["JobId"]``. + - **List + use_bulk_delete=False** -> one DELETE per record. + + :param table: Table schema name. + :type table: :class:`str` + :param ids: Single GUID or list of GUIDs. + :type ids: str or list[str] + :param use_bulk_delete: When True (default) and ``ids`` is a list, use the + BulkDelete action. When False, delete records individually. + :type use_bulk_delete: :class:`bool` + + + + .. py:method:: get(table: str, record_id: str, *, select: Optional[List[str]] = None) -> None + + Add a single-record get operation to the batch. + + .. deprecated:: + Use :meth:`retrieve` instead. ``batch.records.get()`` is deprecated + and will be removed in a future release. + + :param table: Table schema name. + :type table: :class:`str` + :param record_id: GUID of the record to retrieve. + :type record_id: :class:`str` + :param select: Optional list of column names to include. + :type select: list[str] or None + + + + .. py:method:: upsert(table: str, items: List[Union[PowerPlatform.Dataverse.models.upsert.UpsertItem, Dict[str, Any]]]) -> None + + Add an upsert operation to the batch. + + Mirrors :meth:`~PowerPlatform.Dataverse.operations.records.RecordOperations.upsert`: + a single item becomes a PATCH request using the alternate key; multiple items + become one ``UpsertMultiple`` POST. + + Each item must be a :class:`~PowerPlatform.Dataverse.models.upsert.UpsertItem` + or a plain ``dict`` with ``"alternate_key"`` and ``"record"`` keys (both dicts). + + :param table: Table schema name (e.g. ``"account"``). + :type table: :class:`str` + :param items: Non-empty list of :class:`~PowerPlatform.Dataverse.models.upsert.UpsertItem` + instances or equivalent dicts. + :type items: list[~PowerPlatform.Dataverse.models.upsert.UpsertItem] + + :raises TypeError: If ``items`` is not a non-empty list, or if any element is + neither a :class:`~PowerPlatform.Dataverse.models.upsert.UpsertItem` nor a + dict with ``"alternate_key"`` and ``"record"`` keys. + + Example:: + + from PowerPlatform.Dataverse.models import UpsertItem + + batch.records.upsert("account", [ + UpsertItem( + alternate_key={"accountnumber": "ACC-001"}, + record={"name": "Contoso Ltd"}, + ), + UpsertItem( + alternate_key={"accountnumber": "ACC-002"}, + record={"name": "Fabrikam Inc"}, + ), + ]) + + + + .. py:method:: retrieve(table: str, record_id: str, *, select: Optional[List[str]] = None, expand: Optional[List[str]] = None, include_annotations: Optional[str] = None) -> None + + Add a single-record retrieve operation to the batch. + + GA replacement for the deprecated :meth:`get`. Enqueues a GET request + for one record by its GUID. The response body will be available in + :attr:`~PowerPlatform.Dataverse.models.batch.BatchItemResponse.data` + after :meth:`BatchRequest.execute`. + + :param table: Table schema name (e.g. ``"account"``). + :type table: :class:`str` + :param record_id: GUID of the record to retrieve. + :type record_id: :class:`str` + :param select: Optional list of column logical names to include. + :type select: list[str] or None + :param expand: Optional list of navigation properties to expand. + Navigation property names are case-sensitive and must match the + entity's ``$metadata``. + :type expand: list[str] or None + :param include_annotations: OData annotation pattern for the + ``Prefer: odata.include-annotations`` header (e.g. ``"*"`` or + ``"OData.Community.Display.V1.FormattedValue"``), or ``None``. + :type include_annotations: :class:`str` or None + + Example:: + + batch = client.batch.new() + batch.records.retrieve( + "account", account_id, + select=["name", "statuscode"], + expand=["primarycontactid"], + include_annotations="OData.Community.Display.V1.FormattedValue", + ) + result = batch.execute() + record = result.responses[0].data + contact = (record.get("primarycontactid") or {}) + print(contact.get("fullname")) + + + + .. py:method:: list(table: str, *, filter: Optional[Union[str, FilterExpression]] = None, select: Optional[List[str]] = None, orderby: Optional[List[str]] = None, top: Optional[int] = None, expand: Optional[List[str]] = None, page_size: Optional[int] = None, count: bool = False, include_annotations: Optional[str] = None) -> None + + Add a multi-record list operation to the batch (single page, no pagination). + + Enqueues a GET request for multiple records. Because batch requests are + a single HTTP round-trip, pagination (``@odata.nextLink``) is **not** + supported — use ``top`` to bound the result size, or rely on the + server's default page limit. + + The response body (``{"value": [...]}`` JSON) will be available in + :attr:`~PowerPlatform.Dataverse.models.batch.BatchItemResponse.data` + after :meth:`BatchRequest.execute`. + + :param table: Table schema name (e.g. ``"account"``). + :type table: :class:`str` + :param filter: Optional OData ``$filter`` expression or :class:`~PowerPlatform.Dataverse.models.filters.FilterExpression`. + :type filter: str or FilterExpression or None + :param select: Optional list of column logical names to include. + :type select: list[str] or None + :param orderby: Optional list of sort expressions (e.g. ``["name asc"]``). + :type orderby: list[str] or None + :param top: Maximum number of records to return. + :type top: int or None + :param expand: Optional list of navigation properties to expand. + :type expand: list[str] or None + :param page_size: Per-page size hint via ``Prefer: odata.maxpagesize``. + :type page_size: int or None + :param count: If ``True``, adds ``$count=true`` to the request. + :type count: bool + :param include_annotations: OData annotation pattern for the + ``Prefer: odata.include-annotations`` header, or ``None``. + :type include_annotations: :class:`str` or None + + Example:: + + batch = client.batch.new() + batch.records.list( + "account", + filter="statecode eq 0", + select=["name", "statuscode"], + orderby=["name asc"], + top=50, + include_annotations="OData.Community.Display.V1.FormattedValue", + ) + result = batch.execute() + records = result.responses[0].data.get("value", []) + + + +.. py:class:: BatchTableOperations(batch: _BatchContext) + + Table metadata operations on a :class:`BatchRequest`. + + Mirrors ``client.tables`` exactly: same method names, same signatures. + All methods return ``None``; results arrive via + :class:`~PowerPlatform.Dataverse.models.batch.BatchResult`. + + .. note:: + ``tables.delete``, ``tables.add_columns``, and ``tables.remove_columns`` + require a metadata lookup (GET ``EntityDefinitions``) at + :meth:`BatchRequest.execute` time to resolve the table's MetadataId. + This lookup is transparent to the caller. + + .. note:: + ``tables.add_columns`` and ``tables.remove_columns`` each produce one + batch item per column, so they contribute multiple entries to + :attr:`~PowerPlatform.Dataverse.models.batch.BatchResult.responses`. + + Do not instantiate directly; use ``batch.tables``. + + + .. py:method:: create(table: str, columns: Dict[str, Any], *, solution: Optional[str] = None, primary_column: Optional[str] = None, display_name: Optional[str] = None) -> None + + Add a table-create operation to the batch. + + .. note:: + The pre-existence check performed by ``client.tables.create`` is skipped + in batch mode. If the table already exists the server returns an error + in the corresponding :class:`~PowerPlatform.Dataverse.models.batch.BatchItemResponse`. + + :param table: Schema name of the new table (e.g. ``"new_Product"``). + :type table: :class:`str` + :param columns: Mapping of column schema names to type strings or Enum subclasses. + :type columns: dict[str, typing.Any] + :param solution: Optional solution unique name. + :type solution: str or None + :param primary_column: Optional primary column schema name. + :type primary_column: str or None + :param display_name: Human-readable display name for the table. + When omitted, defaults to the table schema name. + :type display_name: str or None + + + + .. py:method:: delete(table: str) -> None + + Add a table-delete operation to the batch. + + The table's ``MetadataId`` is resolved via a GET request at execute time. + + :param table: Schema name of the table to delete. + :type table: :class:`str` + + + + .. py:method:: get(table: str) -> None + + Add a table-metadata-get operation to the batch. + + The response will be in ``BatchItemResponse.data`` after execute. + + :param table: Schema name of the table. + :type table: :class:`str` + + + + .. py:method:: list(*, filter: Optional[str] = None, select: Optional[List[str]] = None) -> None + + Add a list-all-tables operation to the batch. + + Mirrors ``client.tables.list()``. Supply an optional OData + ``$filter`` expression to further narrow the results (combined with + ``IsPrivate eq false`` using ``and``). ``select`` projects + specific property names via ``$select``. + + The response will be in ``BatchItemResponse.data`` after execute. + + :param filter: Additional OData ``$filter`` expression. + :type filter: str or None + :param select: List of property names for ``$select``. + :type select: list[str] or None + + + + .. py:method:: add_columns(table: str, columns: Dict[str, Any]) -> None + + Add column-create operations to the batch (one per column). + + The table's ``MetadataId`` is resolved at execute time. Each column + produces one entry in :attr:`~PowerPlatform.Dataverse.models.batch.BatchResult.responses`. + + :param table: Schema name of the target table. + :type table: :class:`str` + :param columns: Mapping of column schema names to type strings or Enum subclasses. + :type columns: dict[str, typing.Any] + + + + .. py:method:: remove_columns(table: str, columns: Union[str, List[str]]) -> None + + Add column-delete operations to the batch (one per column). + + The table's ``MetadataId`` and each column's ``MetadataId`` are resolved + at execute time. Each column produces one entry in + :attr:`~PowerPlatform.Dataverse.models.batch.BatchResult.responses`. + + :param table: Schema name of the target table. + :type table: :class:`str` + :param columns: Column schema name or list of column schema names to remove. + :type columns: str or list[str] + + + + .. py:method:: create_one_to_many_relationship(lookup: PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata, relationship: PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata, *, solution: Optional[str] = None) -> None + + Add a one-to-many relationship creation to the batch. + + :param lookup: Lookup attribute metadata. + :type lookup: ~PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata + :param relationship: Relationship metadata. + :type relationship: ~PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata + :param solution: Optional solution unique name. + :type solution: str or None + + + + .. py:method:: create_many_to_many_relationship(relationship: PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata, *, solution: Optional[str] = None) -> None + + Add a many-to-many relationship creation to the batch. + + :param relationship: Relationship metadata. + :type relationship: ~PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata + :param solution: Optional solution unique name. + :type solution: str or None + + + + .. py:method:: delete_relationship(relationship_id: str) -> None + + Add a relationship-delete operation to the batch. + + :param relationship_id: GUID of the relationship metadata to delete. + :type relationship_id: :class:`str` + + + + .. py:method:: get_relationship(schema_name: str) -> None + + Add a relationship-metadata-get operation to the batch. + + The response will be in ``BatchItemResponse.data`` after execute. + + :param schema_name: Schema name of the relationship. + :type schema_name: :class:`str` + + + + .. py:method:: create_lookup_field(referencing_table: str, lookup_field_name: str, referenced_table: str, *, display_name: Optional[str] = None, description: Optional[str] = None, required: bool = False, cascade_delete: str = CASCADE_BEHAVIOR_REMOVE_LINK, solution: Optional[str] = None, language_code: int = 1033) -> None + + Add a lookup field creation to the batch (convenience wrapper for + :meth:`create_one_to_many_relationship`). + + :param referencing_table: Logical name of the child (many) table. + :type referencing_table: :class:`str` + :param lookup_field_name: Schema name for the lookup field. + :type lookup_field_name: :class:`str` + :param referenced_table: Logical name of the parent (one) table. + :type referenced_table: :class:`str` + :param display_name: Display name for the lookup field. + :type display_name: str or None + :param description: Optional description. + :type description: str or None + :param required: Whether the lookup is required. + :type required: :class:`bool` + :param cascade_delete: Delete cascade behaviour. + :type cascade_delete: :class:`str` + :param solution: Optional solution unique name. + :type solution: str or None + :param language_code: Language code for labels (default 1033). + :type language_code: :class:`int` + + + +.. py:class:: BatchQueryOperations(batch: _BatchContext) + + Query operations on a :class:`BatchRequest`. + + Mirrors ``client.query`` exactly: same method names, same signatures. + All methods return ``None``; results arrive via + :class:`~PowerPlatform.Dataverse.models.batch.BatchResult`. + + Do not instantiate directly; use ``batch.query``. + + + .. py:method:: sql(sql: str) -> None + + Add a SQL SELECT query to the batch. + + Mirrors :meth:`~PowerPlatform.Dataverse.operations.query.QueryOperations.sql`. + The entity set is resolved from the table name in the SQL statement at + :meth:`BatchRequest.execute` time. + + :param sql: A single ``SELECT`` statement within the Dataverse-supported subset. + :type sql: :class:`str` + + :raises ~PowerPlatform.Dataverse.core.errors.ValidationError: + If ``sql`` is not a non-empty string. + + Example:: + + batch.query.sql("SELECT accountid, name FROM account WHERE name = 'Contoso'") + + + +.. py:class:: BatchDataFrameOperations(batch: _BatchContext) + + DataFrame-oriented wrappers for batch record operations. + + Provides :meth:`create`, :meth:`update`, and :meth:`delete` that accept + ``pandas.DataFrame`` / ``pandas.Series`` inputs and convert them to standard + dicts before enqueueing on the batch. This lets data-science callers feed + DataFrames directly into a batch without manual conversion. + + Accessed via ``batch.dataframe``. + + Example:: + + import pandas as pd + + batch = client.batch.new() + df = pd.DataFrame([ + {"name": "Contoso", "telephone1": "555-0100"}, + {"name": "Fabrikam", "telephone1": "555-0200"}, + ]) + batch.dataframe.create("account", df) + result = batch.execute() + + + .. py:method:: create(table: str, records: pandas.DataFrame) -> None + + Enqueue record creates from a pandas DataFrame. + + Each row becomes a record. All rows are bundled in a single + ``CreateMultiple`` batch item (one HTTP request in the batch). + + :param table: Table schema name (e.g. ``"account"``). + :type table: :class:`str` + :param records: DataFrame where each row is a record to create. + :type records: ~pandas.DataFrame + + :raises TypeError: If ``records`` is not a pandas DataFrame. + :raises ValueError: If ``records`` is empty or any row has no non-null values. + + Example:: + + df = pd.DataFrame([{"name": "Contoso"}, {"name": "Fabrikam"}]) + batch.dataframe.create("account", df) + + + + .. py:method:: update(table: str, changes: pandas.DataFrame, id_column: str, clear_nulls: bool = False) -> None + + Enqueue record updates from a pandas DataFrame. + + Each row represents an update. The ``id_column`` specifies which + column contains the record GUIDs. + + :param table: Table schema name (e.g. ``"account"``). + :type table: :class:`str` + :param changes: DataFrame where each row contains a record GUID and + the fields to update. + :type changes: ~pandas.DataFrame + :param id_column: Name of the DataFrame column containing record GUIDs. + :type id_column: :class:`str` + :param clear_nulls: When ``False`` (default), NaN/None values are + skipped. When ``True``, NaN/None sends ``null`` to clear the field. + :type clear_nulls: :class:`bool` + + :raises TypeError: If ``changes`` is not a pandas DataFrame. + :raises ValueError: If ``changes`` is empty, ``id_column`` is missing, + or IDs are invalid. + + Example:: + + df = pd.DataFrame([ + {"accountid": "guid-1", "telephone1": "555-0100"}, + {"accountid": "guid-2", "telephone1": "555-0200"}, + ]) + batch.dataframe.update("account", df, id_column="accountid") + + + + .. py:method:: delete(table: str, ids: pandas.Series, use_bulk_delete: bool = True) -> None + + Enqueue record deletes from a pandas Series of GUIDs. + + :param table: Table schema name (e.g. ``"account"``). + :type table: :class:`str` + :param ids: Series of record GUIDs to delete. + :type ids: ~pandas.Series + :param use_bulk_delete: When ``True`` (default) and ``ids`` has multiple + values, use the ``BulkDelete`` action. + :type use_bulk_delete: :class:`bool` + + :raises TypeError: If ``ids`` is not a pandas Series. + :raises ValueError: If ``ids`` contains invalid values. + + Example:: + + ids_series = pd.Series(["guid-1", "guid-2", "guid-3"]) + batch.dataframe.delete("account", ids_series) + + + +.. py:class:: BatchRequest(client: PowerPlatform.Dataverse.client.DataverseClient) + + Builder for constructing and executing a Dataverse OData ``$batch`` request. + + Obtain via :meth:`BatchOperations.new` (``client.batch.new()``). Add operations + through ``records``, ``tables``, ``query``, and ``dataframe``, + optionally group writes + into a :meth:`changeset`, then call :meth:`execute`. + + Operations are executed sequentially in the order added. The resulting + :class:`~PowerPlatform.Dataverse.models.batch.BatchResult` contains one + :class:`~PowerPlatform.Dataverse.models.batch.BatchItemResponse` per HTTP + request dispatched (some operations expand to multiple requests). + + .. note:: + Maximum 1000 HTTP operations per batch. + + Example:: + + batch = client.batch.new() + batch.records.create("account", {"name": "Contoso"}) + batch.tables.get("account") + with batch.changeset() as cs: + ref = cs.records.create("contact", {"firstname": "Alice"}) + cs.records.update("account", account_id, { + "primarycontactid@odata.bind": ref + }) + result = batch.execute() + + + .. py:attribute:: records + + + .. py:attribute:: tables + + + .. py:attribute:: query + + + .. py:attribute:: dataframe + + + .. py:method:: changeset() -> ChangeSet + + Create a new :class:`ChangeSet` attached to this batch. + + The changeset is added to the batch immediately. Operations added to + the returned :class:`ChangeSet` via ``cs.records.*`` execute atomically. + + :returns: A new :class:`ChangeSet` ready to receive operations. + + Example:: + + with batch.changeset() as cs: + cs.records.create("account", {"name": "ACME"}) + cs.records.create("contact", {"firstname": "Bob"}) + + + + .. py:method:: execute(*, continue_on_error: bool = False) -> PowerPlatform.Dataverse.models.batch.BatchResult + + Submit the batch to Dataverse and return all responses. + + :param continue_on_error: When False (default), Dataverse stops at the + first failure and returns that operation's error as a 4xx response. + When True, ``Prefer: odata.continue-on-error`` is sent and all + operations are attempted. + :returns: :class:`~PowerPlatform.Dataverse.models.batch.BatchResult` + with one entry per HTTP operation in submission order. + :raises ValidationError: If the batch exceeds 1000 operations or an + unsupported column type is specified. + :raises MetadataError: If metadata pre-resolution fails (table or + column not found) for ``tables.delete``, ``tables.add_columns``, + or ``tables.remove_columns``. + :raises HttpError: On HTTP-level failures (auth, server error, etc.) + that prevent the batch from executing. + + + +.. py:class:: BatchOperations(client: PowerPlatform.Dataverse.client.DataverseClient) + + Namespace for batch operations (``client.batch``). + + Accessed via ``client.batch``. Use :meth:`new` to create a + :class:`BatchRequest` builder. + + :param client: The parent :class:`~PowerPlatform.Dataverse.client.DataverseClient` instance. + + Example:: + + batch = client.batch.new() + batch.records.create("account", {"name": "Fabrikam"}) + result = batch.execute() + + + .. py:method:: new() -> BatchRequest + + Create a new empty :class:`BatchRequest` builder. + + :returns: An empty :class:`BatchRequest`. + + + diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/operations/dataframe/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/operations/dataframe/index.rst new file mode 100644 index 00000000..d240dee2 --- /dev/null +++ b/docs_local/autoapi/PowerPlatform/Dataverse/operations/dataframe/index.rst @@ -0,0 +1,279 @@ +PowerPlatform.Dataverse.operations.dataframe +============================================ + +.. py:module:: PowerPlatform.Dataverse.operations.dataframe + +.. autoapi-nested-parse:: + + DataFrame CRUD operations namespace for the Dataverse SDK. + + + +Classes +------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.operations.dataframe.DataFrameOperations + + +Module Contents +--------------- + +.. py:class:: DataFrameOperations(client: PowerPlatform.Dataverse.client.DataverseClient) + + Namespace for pandas DataFrame CRUD operations. + + Accessed via ``client.dataframe``. Provides DataFrame-oriented wrappers + around the record-level CRUD operations. + + :param client: The parent :class:`~PowerPlatform.Dataverse.client.DataverseClient` instance. + :type client: ~PowerPlatform.Dataverse.client.DataverseClient + + Example:: + + import pandas as pd + + client = DataverseClient(base_url, credential) + + # Query records as a DataFrame + df = client.dataframe.get("account", select=["name"], top=100) + + # Create records from a DataFrame + new_df = pd.DataFrame([{"name": "Contoso"}, {"name": "Fabrikam"}]) + new_df["accountid"] = client.dataframe.create("account", new_df) + + # Update records + new_df["telephone1"] = ["555-0100", "555-0200"] + client.dataframe.update("account", new_df, id_column="accountid") + + # Delete records + client.dataframe.delete("account", new_df["accountid"]) + + + .. py:method:: sql(sql: str) -> pandas.DataFrame + + Execute a SQL query and return the results as a pandas DataFrame. + + Delegates to :meth:`~PowerPlatform.Dataverse.operations.query.QueryOperations.sql` + and converts the list of records into a single DataFrame. + + :param sql: Supported SQL SELECT statement. + :type sql: :class:`str` + + :return: DataFrame containing all result rows. Returns an empty + DataFrame when no rows match. + :rtype: ~pandas.DataFrame + + :raises ~PowerPlatform.Dataverse.core.errors.ValidationError: + If ``sql`` is not a string or is empty. + + .. rubric:: Example + + SQL query to DataFrame:: + + df = client.dataframe.sql( + "SELECT TOP 100 name, revenue FROM account " + "WHERE statecode = 0 ORDER BY revenue" + ) + print(f"Got {len(df)} rows") + print(df.head()) + + Aggregate query to DataFrame:: + + df = client.dataframe.sql( + "SELECT a.name, COUNT(c.contactid) as cnt " + "FROM account a " + "JOIN contact c ON a.accountid = c.parentcustomerid " + "GROUP BY a.name" + ) + + + + .. py:method:: get(table: str, record_id: Optional[str] = None, select: Optional[List[str]] = None, filter: Optional[str] = None, orderby: Optional[List[str]] = None, top: Optional[int] = None, expand: Optional[List[str]] = None, page_size: Optional[int] = None, count: bool = False, include_annotations: Optional[str] = None) -> pandas.DataFrame + + Fetch records and return as a single pandas DataFrame. + + When ``record_id`` is provided, returns a single-row DataFrame. + When ``record_id`` is None, internally iterates all pages and returns one + consolidated DataFrame. + + :param table: Schema name of the table (e.g. ``"account"`` or ``"new_MyTestTable"``). + :type table: :class:`str` + :param record_id: Optional GUID to fetch a specific record. If None, queries multiple records. + :type record_id: :class:`str` or None + :param select: Optional list of attribute logical names to retrieve. + :type select: list[str] or None + :param filter: Optional OData filter string. Column names must use exact lowercase logical names. + :type filter: :class:`str` or None + :param orderby: Optional list of attributes to sort by. + :type orderby: list[str] or None + :param top: Optional maximum number of records to return. + :type top: :class:`int` or None + :param expand: Optional list of navigation properties to expand (case-sensitive). + :type expand: list[str] or None + :param page_size: Optional number of records per page for pagination. + :type page_size: :class:`int` or None + :param count: If ``True``, adds ``$count=true`` to include a total + record count in the response. + :type count: :class:`bool` + :param include_annotations: OData annotation pattern for the + ``Prefer: odata.include-annotations`` header (e.g. ``"*"`` or + ``"OData.Community.Display.V1.FormattedValue"``), or ``None``. + :type include_annotations: :class:`str` or None + + :return: DataFrame containing all matching records. Returns an empty DataFrame + when no records match. + :rtype: ~pandas.DataFrame + + :raises ValueError: If ``record_id`` is not a non-empty string, or if + query parameters (``filter``, ``orderby``, ``top``, ``expand``, + ``page_size``) are provided alongside ``record_id``. + + .. tip:: + For large tables, use ``top`` or ``filter`` to limit the result set. + + .. rubric:: Example + + Fetch a single record as a DataFrame:: + + df = client.dataframe.get("account", record_id=account_id, select=["name", "telephone1"]) + print(df) + + Query with filtering:: + + df = client.dataframe.get("account", filter="statecode eq 0", select=["name"]) + print(f"Got {len(df)} active accounts") + + Limit result size:: + + df = client.dataframe.get("account", select=["name"], top=100) + + + + .. py:method:: create(table: str, records: pandas.DataFrame) -> pandas.Series + + Create records from a pandas DataFrame. + + :param table: Schema name of the table (e.g. ``"account"`` or ``"new_MyTestTable"``). + :type table: :class:`str` + :param records: DataFrame where each row is a record to create. + :type records: ~pandas.DataFrame + + :return: Series of created record GUIDs, aligned with the input DataFrame index. + :rtype: ~pandas.Series + + :raises TypeError: If ``records`` is not a pandas DataFrame. + :raises ValueError: If ``records`` is empty or the number of returned + IDs does not match the number of input rows. + + .. tip:: + All rows are sent in a single ``CreateMultiple`` request. For very + large DataFrames, consider splitting into smaller batches to avoid + request timeouts. + + .. rubric:: Example + + Create records from a DataFrame:: + + import pandas as pd + + df = pd.DataFrame([ + {"name": "Contoso", "telephone1": "555-0100"}, + {"name": "Fabrikam", "telephone1": "555-0200"}, + ]) + df["accountid"] = client.dataframe.create("account", df) + + + + .. py:method:: update(table: str, changes: pandas.DataFrame, id_column: str, clear_nulls: bool = False) -> None + + Update records from a pandas DataFrame. + + Each row in the DataFrame represents an update. The ``id_column`` specifies which + column contains the record GUIDs. + + :param table: Schema name of the table (e.g. ``"account"`` or ``"new_MyTestTable"``). + :type table: :class:`str` + :param changes: DataFrame where each row contains a record GUID and the fields to update. + :type changes: ~pandas.DataFrame + :param id_column: Name of the DataFrame column containing record GUIDs. + :type id_column: :class:`str` + :param clear_nulls: When ``False`` (default), missing values (NaN/None) are skipped + (the field is left unchanged on the server). When ``True``, missing values are sent + as ``null`` to Dataverse, clearing the field. Use ``True`` only when you intentionally + want NaN/None values to clear fields. + :type clear_nulls: :class:`bool` + + :raises TypeError: If ``changes`` is not a pandas DataFrame. + :raises ValueError: If ``changes`` is empty, ``id_column`` is not found in the + DataFrame, ``id_column`` contains invalid (non-string, empty, or whitespace-only) + values, or no updatable columns exist besides ``id_column``. + When ``clear_nulls`` is ``False`` (default), rows where all change values + are NaN/None produce empty patches and are silently skipped. If all rows + are skipped, the method returns without making an API call. When + ``clear_nulls`` is ``True``, NaN/None values become explicit nulls, so + rows are never skipped. + + .. tip:: + All rows are sent in a single ``UpdateMultiple`` request (or a + single PATCH for one row). For very large DataFrames, consider + splitting into smaller batches to avoid request timeouts. + + .. rubric:: Example + + Update records with different values per row:: + + import pandas as pd + + df = pd.DataFrame([ + {"accountid": "guid-1", "telephone1": "555-0100"}, + {"accountid": "guid-2", "telephone1": "555-0200"}, + ]) + client.dataframe.update("account", df, id_column="accountid") + + Broadcast the same change to all records:: + + df = pd.DataFrame({"accountid": ["guid-1", "guid-2", "guid-3"]}) + df["websiteurl"] = "https://example.com" + client.dataframe.update("account", df, id_column="accountid") + + Clear a field by setting clear_nulls=True:: + + df = pd.DataFrame([{"accountid": "guid-1", "websiteurl": None}]) + client.dataframe.update("account", df, id_column="accountid", clear_nulls=True) + + + + .. py:method:: delete(table: str, ids: pandas.Series, use_bulk_delete: bool = True) -> Optional[str] + + Delete records by passing a pandas Series of GUIDs. + + :param table: Schema name of the table (e.g. ``"account"`` or ``"new_MyTestTable"``). + :type table: :class:`str` + :param ids: Series of record GUIDs to delete. + :type ids: ~pandas.Series + :param use_bulk_delete: When ``True`` (default) and ``ids`` contains multiple values, execute the BulkDelete + action and return its async job identifier. When ``False`` each record is deleted sequentially. + :type use_bulk_delete: :class:`bool` + + :raises TypeError: If ``ids`` is not a pandas Series. + :raises ValueError: If ``ids`` contains invalid (non-string, empty, or + whitespace-only) values. + + :return: BulkDelete job ID when deleting multiple records via BulkDelete; + ``None`` when deleting a single record, using sequential deletion, or + when ``ids`` is empty. + :rtype: :class:`str` or None + + .. rubric:: Example + + Delete records using a Series:: + + import pandas as pd + + ids = pd.Series(["guid-1", "guid-2", "guid-3"]) + client.dataframe.delete("account", ids) + + + diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/operations/files/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/operations/files/index.rst new file mode 100644 index 00000000..ad07ef55 --- /dev/null +++ b/docs_local/autoapi/PowerPlatform/Dataverse/operations/files/index.rst @@ -0,0 +1,99 @@ +PowerPlatform.Dataverse.operations.files +======================================== + +.. py:module:: PowerPlatform.Dataverse.operations.files + +.. autoapi-nested-parse:: + + File operations namespace for the Dataverse SDK. + + + +Classes +------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.operations.files.FileOperations + + +Module Contents +--------------- + +.. py:class:: FileOperations(client: PowerPlatform.Dataverse.client.DataverseClient) + + Namespace for file operations. + + Accessed via ``client.files``. Provides file upload operations for + Dataverse file columns. + + :param client: The parent :class:`~PowerPlatform.Dataverse.client.DataverseClient` instance. + :type client: ~PowerPlatform.Dataverse.client.DataverseClient + + Example:: + + client = DataverseClient(base_url, credential) + + client.files.upload( + "account", account_id, "new_Document", "/path/to/file.pdf" + ) + + + .. py:method:: upload(table: str, record_id: str, file_column: str, path: str, *, mode: Optional[str] = None, mime_type: Optional[str] = None, if_none_match: bool = True) -> None + + Upload a file to a Dataverse file column. + + :param table: Schema name of the table (e.g. ``"account"`` or + ``"new_MyTestTable"``). + :type table: :class:`str` + :param record_id: GUID of the target record. + :type record_id: :class:`str` + :param file_column: Schema name of the file column attribute (e.g., + ``"new_Document"``). If the column doesn't exist, it will be + created automatically. + :type file_column: :class:`str` + :param path: Local filesystem path to the file. The stored filename + will be the basename of this path. + :type path: :class:`str` + :param mode: Upload strategy: ``"auto"`` (default), ``"small"``, or + ``"chunk"``. Auto mode selects small or chunked upload based on + file size. + :type mode: :class:`str` or None + :param mime_type: Explicit MIME type to store with the file (e.g. + ``"application/pdf"``). If not provided, defaults to + ``"application/octet-stream"``. + :type mime_type: :class:`str` or None + :param if_none_match: When True (default), sends + ``If-None-Match: null`` header to only succeed if the column is + currently empty. Set False to always overwrite using + ``If-Match: *``. + :type if_none_match: :class:`bool` + + :raises ~PowerPlatform.Dataverse.core.errors.HttpError: + If the upload fails or the file column is not empty when + ``if_none_match=True``. + :raises FileNotFoundError: If the specified file path does not exist. + + .. rubric:: Example + + Upload a PDF file:: + + client.files.upload( + "account", + account_id, + "new_Contract", + "/path/to/contract.pdf", + mime_type="application/pdf", + ) + + Upload with auto mode selection:: + + client.files.upload( + "email", + email_id, + "new_Attachment", + "/path/to/large_file.zip", + ) + + + diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/operations/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/operations/index.rst new file mode 100644 index 00000000..cbb14381 --- /dev/null +++ b/docs_local/autoapi/PowerPlatform/Dataverse/operations/index.rst @@ -0,0 +1,28 @@ +PowerPlatform.Dataverse.operations +================================== + +.. py:module:: PowerPlatform.Dataverse.operations + +.. autoapi-nested-parse:: + + Operation namespaces for the Dataverse SDK. + + This module contains the operation namespace classes that organize + SDK operations into logical groups: records, query, and tables. + + + +Submodules +---------- + +.. toctree:: + :maxdepth: 1 + + /autoapi/PowerPlatform/Dataverse/operations/batch/index + /autoapi/PowerPlatform/Dataverse/operations/dataframe/index + /autoapi/PowerPlatform/Dataverse/operations/files/index + /autoapi/PowerPlatform/Dataverse/operations/query/index + /autoapi/PowerPlatform/Dataverse/operations/records/index + /autoapi/PowerPlatform/Dataverse/operations/tables/index + + diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/operations/query/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/operations/query/index.rst new file mode 100644 index 00000000..740ab5b4 --- /dev/null +++ b/docs_local/autoapi/PowerPlatform/Dataverse/operations/query/index.rst @@ -0,0 +1,345 @@ +PowerPlatform.Dataverse.operations.query +======================================== + +.. py:module:: PowerPlatform.Dataverse.operations.query + +.. autoapi-nested-parse:: + + Query operations namespace for the Dataverse SDK. + + + +Classes +------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.operations.query.QueryOperations + + +Module Contents +--------------- + +.. py:class:: QueryOperations(client: PowerPlatform.Dataverse.client.DataverseClient) + + Namespace for query operations. + + Accessed via ``client.query``. Provides query and search operations + against Dataverse tables. + + :param client: The parent :class:`~PowerPlatform.Dataverse.client.DataverseClient` instance. + :type client: ~PowerPlatform.Dataverse.client.DataverseClient + + Example:: + + from PowerPlatform.Dataverse.models.filters import col + + client = DataverseClient(base_url, credential) + + # Fluent query builder (recommended) + for record in (client.query.builder("account") + .select("name", "revenue") + .where(col("statecode") == 0) + .order_by("revenue", descending=True) + .top(100) + .execute()): + print(record["name"]) + + # SQL query + rows = client.query.sql("SELECT TOP 10 name FROM account ORDER BY name") + for row in rows: + print(row["name"]) + + + .. py:method:: builder(table: str) -> PowerPlatform.Dataverse.models.query_builder.QueryBuilder + + Create a fluent query builder for the specified table. + + Returns a :class:`~PowerPlatform.Dataverse.models.query_builder.QueryBuilder` + that can be chained with filter, select, and order methods, then + executed directly via ``.execute()``. + + :param table: Table schema name (e.g. ``"account"``). + :type table: :class:`str` + :return: A QueryBuilder instance bound to this client. + :rtype: ~PowerPlatform.Dataverse.models.query_builder.QueryBuilder + + .. rubric:: Example + + Build and execute a query fluently:: + + from PowerPlatform.Dataverse.models.filters import col + + for record in (client.query.builder("account") + .select("name", "revenue") + .where(col("statecode") == 0) + .where(col("revenue") > 1_000_000) + .order_by("revenue", descending=True) + .top(100) + .page_size(50) + .execute()): + print(record["name"]) + + With composable expression tree:: + + from PowerPlatform.Dataverse.models.filters import col + + for record in (client.query.builder("account") + .where((col("statecode") == 0) | (col("statecode") == 1)) + .where(col("revenue") > 100_000) + .execute()): + print(record["name"]) + + + + .. py:method:: sql(sql: str) -> List[PowerPlatform.Dataverse.models.record.Record] + + Execute a read-only SQL query using the Dataverse Web API. + + The Dataverse SQL endpoint supports a broad subset of T-SQL:: + + SELECT / SELECT DISTINCT / SELECT TOP N (0-5000) + FROM table [alias] + INNER JOIN / LEFT JOIN (multi-table, no depth limit) + WHERE (=, !=, >, <, >=, <=, LIKE, IN, NOT IN, IS NULL, + IS NOT NULL, BETWEEN, AND, OR, nested parentheses) + GROUP BY column + ORDER BY column [ASC|DESC] + OFFSET n ROWS FETCH NEXT m ROWS ONLY + COUNT(*), SUM(), AVG(), MIN(), MAX() + + ``SELECT *`` is not supported -- specify column names explicitly. + Use :meth:`sql_columns` to discover available column names for a table. + + Not supported: SELECT *, subqueries, CTE, HAVING, UNION, + RIGHT/FULL/CROSS JOIN, CASE, COALESCE, window functions, + string/date/math functions, INSERT/UPDATE/DELETE. For writes, use + ``client.records`` methods. + + :param sql: Supported SQL SELECT statement. + :type sql: :class:`str` + + :return: List of :class:`~PowerPlatform.Dataverse.models.record.Record` + objects. Returns an empty list when no rows match. + :rtype: list[~PowerPlatform.Dataverse.models.record.Record] + + :raises ~PowerPlatform.Dataverse.core.errors.ValidationError: + If ``sql`` is not a string or is empty. + + .. rubric:: Example + + Basic query:: + + rows = client.query.sql( + "SELECT TOP 10 name FROM account ORDER BY name" + ) + + JOIN with aggregation:: + + rows = client.query.sql( + "SELECT a.name, COUNT(c.contactid) as cnt " + "FROM account a " + "JOIN contact c ON a.accountid = c.parentcustomerid " + "GROUP BY a.name" + ) + + + + .. py:method:: fetchxml(xml: str) -> PowerPlatform.Dataverse.models.fetchxml_query.FetchXmlQuery + + Return an inert :class:`~PowerPlatform.Dataverse.models.fetchxml_query.FetchXmlQuery` object. + + No HTTP request is made until + :meth:`~PowerPlatform.Dataverse.models.fetchxml_query.FetchXmlQuery.execute` + or + :meth:`~PowerPlatform.Dataverse.models.fetchxml_query.FetchXmlQuery.execute_pages` + is called on the returned object. + + Use for SQL-JOIN scenarios, aggregate queries, or other operations that + the OData builder endpoint cannot express. + + :param xml: Well-formed FetchXML query string. The root ```` + element determines the entity set endpoint. + :type xml: :class:`str` + :return: Inert query object with ``.execute()`` and ``.execute_pages()`` methods. + :rtype: :class:`~PowerPlatform.Dataverse.models.fetchxml_query.FetchXmlQuery` + :raises ValueError: If the FetchXML is missing a root ```` element + or the entity ``name`` attribute. + + Example:: + + query = client.query.fetchxml(""" + + + + + + + + + """) + + # Eager — collect all pages: + result = query.execute() + df = result.to_dataframe() + + # Lazy — process one page at a time: + for page in query.execute_pages(): + process(page.to_dataframe()) + + + + .. py:method:: sql_columns(table: str, *, include_system: bool = False) -> List[Dict[str, Any]] + + Return a simplified list of SQL-usable columns for a table. + + Each dict contains ``name`` (logical name for SQL), ``type`` + (Dataverse attribute type), ``is_pk`` (primary key flag), and + ``label`` (display name). Virtual columns are always excluded + because the SQL endpoint cannot query them. + + :param table: Schema name of the table (e.g. ``"account"``). + :type table: :class:`str` + :param include_system: When ``False`` (default), columns that end + with common system suffixes (``_base``, ``versionnumber``, + ``timezoneruleversionnumber``, ``utcconversiontimezonecode``, + ``importsequencenumber``, ``overriddencreatedon``) are excluded. + :type include_system: :class:`bool` + + :return: List of column metadata dicts. + :rtype: list[dict[str, typing.Any]] + + Example:: + + cols = client.query.sql_columns("account") + for c in cols: + print(f"{c['name']:30s} {c['type']:20s} PK={c['is_pk']}") + + + + .. py:method:: odata_select(table: str, *, include_system: bool = False) -> List[str] + + Return a list of column logical names suitable for ``$select``. + + Can be passed directly to ``client.records.get(table, select=...)``. + + :param table: Schema name of the table (e.g. ``"account"``). + :type table: :class:`str` + :param include_system: Include system columns (default ``False``). + :type include_system: :class:`bool` + + :return: List of lowercase column logical names. + :rtype: list[str] + + Example:: + + cols = client.query.odata_select("account") + for page in client.records.get("account", select=cols, top=10): + for r in page: + print(r) + + + + .. py:method:: odata_expands(table: str) -> List[Dict[str, Any]] + + Discover all ``$expand`` navigation properties from a table. + + Returns entries for each outgoing lookup (single-valued navigation + property). Each entry contains the exact PascalCase navigation + property name needed for ``$expand`` and ``@odata.bind``, plus + the target entity set name. + + :param table: Schema name of the table (e.g. ``"contact"``). + :type table: :class:`str` + + :return: List of dicts, each with: + + - ``nav_property`` -- PascalCase navigation property for $expand + - ``target_table`` -- target entity logical name + - ``target_entity_set`` -- target entity set (for @odata.bind) + - ``lookup_attribute`` -- the lookup column logical name + - ``relationship`` -- relationship schema name + + :rtype: list[dict[str, typing.Any]] + + Example:: + + expands = client.query.odata_expands("contact") + for e in expands: + print(f"expand={e['nav_property']} -> {e['target_table']}") + + # Use in a query + e = next(e for e in expands if e['target_table'] == 'account') + for page in client.records.get("contact", + select=["fullname"], + expand=[e['nav_property']]): + ... + + + + .. py:method:: odata_expand(from_table: str, to_table: str) -> str + + Return the navigation property name to ``$expand`` from one table to another. + + Discovers via relationship metadata. Returns the exact PascalCase + string for the ``expand=`` parameter. + + :param from_table: Schema name of the source table (e.g. ``"contact"``). + :type from_table: :class:`str` + :param to_table: Schema name of the target table (e.g. ``"account"``). + :type to_table: :class:`str` + + :return: The navigation property name (PascalCase). + :rtype: :class:`str` + + :raises ValueError: If no navigation property found for the target. + + Example:: + + nav = client.query.odata_expand("contact", "account") + # Returns e.g. "parentcustomerid_account" + for page in client.records.get("contact", + select=["fullname"], + expand=[nav], + top=5): + for r in page: + acct = r.get(nav) or {} + print(f"{r['fullname']} -> {acct.get('name', 'N/A')}") + + + + .. py:method:: odata_bind(from_table: str, to_table: str, target_id: str) -> Dict[str, str] + + Build an ``@odata.bind`` entry for setting a lookup field. + + Auto-discovers the navigation property name and entity set name + from metadata. Returns a single-entry dict that can be merged + into a create or update payload. + + :param from_table: Schema name of the entity being created/updated. + :type from_table: :class:`str` + :param to_table: Schema name of the target entity the lookup points to. + :type to_table: :class:`str` + :param target_id: GUID of the target record. + :type target_id: :class:`str` + + :return: A dict like ``{"NavProp@odata.bind": "/entityset(guid)"}``. + :rtype: dict[str, str] + + :raises ValueError: If no relationship found between the tables. + + Example:: + + # Instead of manually constructing: + # {"parentcustomerid_account@odata.bind": "/accounts(guid)"} + # Just do: + bind = client.query.odata_bind("contact", "account", acct_id) + client.records.create("contact", { + "firstname": "Jane", + "lastname": "Doe", + **bind, + }) + + + diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/operations/records/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/operations/records/index.rst new file mode 100644 index 00000000..b76711dd --- /dev/null +++ b/docs_local/autoapi/PowerPlatform/Dataverse/operations/records/index.rst @@ -0,0 +1,461 @@ +PowerPlatform.Dataverse.operations.records +========================================== + +.. py:module:: PowerPlatform.Dataverse.operations.records + +.. autoapi-nested-parse:: + + Record CRUD operations namespace for the Dataverse SDK. + + + +Classes +------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.operations.records.RecordOperations + + +Module Contents +--------------- + +.. py:class:: RecordOperations(client: PowerPlatform.Dataverse.client.DataverseClient) + + Namespace for record-level CRUD operations. + + Accessed via ``client.records``. Provides create, update, delete, and get + operations on individual Dataverse records. + + :param client: The parent :class:`~PowerPlatform.Dataverse.client.DataverseClient` instance. + :type client: ~PowerPlatform.Dataverse.client.DataverseClient + + Example:: + + client = DataverseClient(base_url, credential) + + # Create a single record + guid = client.records.create("account", {"name": "Contoso Ltd"}) + + # Get a record + record = client.records.get("account", guid, select=["name"]) + + # Update a record + client.records.update("account", guid, {"telephone1": "555-0100"}) + + # Delete a record + client.records.delete("account", guid) + + + .. py:method:: create(table: str, data: Dict[str, Any]) -> str + create(table: str, data: List[Dict[str, Any]]) -> List[str] + + Create one or more records in a Dataverse table. + + When ``data`` is a single dictionary, creates one record and returns its + GUID as a string. When ``data`` is a list of dictionaries, creates all + records via the ``CreateMultiple`` action and returns a list of GUIDs. + + :param table: Schema name of the table (e.g. ``"account"`` or ``"new_MyTestTable"``). + :type table: :class:`str` + :param data: A single record dictionary or a list of record dictionaries. + Each dictionary maps column schema names to values. + :type data: dict or list[dict] + + :return: A single GUID string for a single record, or a list of GUID + strings for bulk creation. + :rtype: str or list[str] + + :raises TypeError: If ``data`` is not a dict or list[dict]. + + .. rubric:: Example + + Create a single record:: + + guid = client.records.create("account", {"name": "Contoso"}) + print(f"Created: {guid}") + + Create multiple records:: + + guids = client.records.create("account", [ + {"name": "Contoso"}, + {"name": "Fabrikam"}, + ]) + print(f"Created {len(guids)} accounts") + + + + .. py:method:: update(table: str, ids: Union[str, List[str]], changes: Union[Dict[str, Any], List[Dict[str, Any]]]) -> None + + Update one or more records in a Dataverse table. + + Supports three usage patterns: + + 1. **Single** -- ``update("account", "guid", {"name": "New"})`` + 2. **Broadcast** -- ``update("account", [id1, id2], {"status": 1})`` + applies the same changes dict to every ID. + 3. **Paired** -- ``update("account", [id1, id2], [ch1, ch2])`` + applies each changes dict to its corresponding ID (lists must be + equal length). + + :param table: Schema name of the table (e.g. ``"account"``). + :type table: :class:`str` + :param ids: A single GUID string, or a list of GUID strings. + :type ids: str or list[str] + :param changes: A dictionary of field changes (single/broadcast), or a + list of dictionaries (paired, one per ID). + :type changes: dict or list[dict] + + :raises TypeError: If ``ids`` is not str or list[str], or if ``changes`` + does not match the expected pattern. + + .. rubric:: Example + + Single update:: + + client.records.update("account", account_id, {"telephone1": "555-0100"}) + + Broadcast update:: + + client.records.update("account", [id1, id2], {"statecode": 1}) + + Paired update:: + + client.records.update( + "account", + [id1, id2], + [{"name": "Name A"}, {"name": "Name B"}], + ) + + + + .. py:method:: delete(table: str, ids: str) -> None + delete(table: str, ids: List[str], *, use_bulk_delete: bool = True) -> Optional[str] + + Delete one or more records from a Dataverse table. + + When ``ids`` is a single string, deletes that one record. When ``ids`` + is a list, either executes a BulkDelete action (returning the async job + ID) or deletes each record sequentially depending on ``use_bulk_delete``. + + :param table: Schema name of the table (e.g. ``"account"``). + :type table: :class:`str` + :param ids: A single GUID string, or a list of GUID strings. + :type ids: str or list[str] + :param use_bulk_delete: When True (default) and ``ids`` is a list, use + the BulkDelete action and return its async job ID. When False, delete + records one at a time. + :type use_bulk_delete: :class:`bool` + + :return: The BulkDelete job ID when bulk-deleting; otherwise None. + :rtype: :class:`str` or None + + :raises TypeError: If ``ids`` is not str or list[str]. + + .. rubric:: Example + + Delete a single record:: + + client.records.delete("account", account_id) + + Bulk delete:: + + job_id = client.records.delete("account", [id1, id2, id3]) + + + + .. py:method:: get(table: str, record_id: str, *, select: Optional[List[str]] = None) -> PowerPlatform.Dataverse.models.record.Record + get(table: str, *, select: Optional[List[str]] = None, filter: Optional[str] = None, orderby: Optional[List[str]] = None, top: Optional[int] = None, expand: Optional[List[str]] = None, page_size: Optional[int] = None, count: bool = False, include_annotations: Optional[str] = None) -> Iterable[List[PowerPlatform.Dataverse.models.record.Record]] + + Fetch a single record by ID, or fetch multiple records with pagination. + + This method has two usage patterns: + + **Fetch a single record** -- ``get(table, record_id, *, select=...)`` + + Pass ``record_id`` as a positional argument to retrieve one record + and get back a :class:`dict`. Query parameters (``filter``, + ``orderby``, ``top``, ``expand``, ``page_size``) must not be provided. + + **Fetch multiple records** -- ``get(table, *, select=..., filter=..., ...)`` + + Omit ``record_id`` to perform a paginated fetch and get back a + generator that yields one page (list of record dicts) at a time. + Automatically follows ``@odata.nextLink`` for server-side paging. + + :param table: Schema name of the table (e.g. ``"account"`` or + ``"new_MyTestTable"``). + :type table: :class:`str` + :param record_id: GUID of the record to retrieve. When omitted, + performs a multi-record fetch instead. + :type record_id: :class:`str` or None + :param select: Optional list of column logical names to include. + Column names are automatically lowercased. + :type select: list[str] or None + :param filter: Optional OData ``$filter`` expression (e.g. + ``"name eq 'Contoso'"``). Column names in filter expressions must + use exact lowercase logical names. Only used for multi-record + queries. + :type filter: :class:`str` or None + :param orderby: Optional list of sort expressions (e.g. + ``["name asc", "createdon desc"]``). Column names are + automatically lowercased. Only used for multi-record queries. + :type orderby: list[str] or None + :param top: Optional maximum total number of records to return. Only + used for multi-record queries. + :type top: :class:`int` or None + :param expand: Optional list of navigation properties to expand (e.g. + ``["primarycontactid"]``). Case-sensitive; must match + server-defined names exactly. Only used for multi-record queries. + :type expand: list[str] or None + :param page_size: Optional per-page size hint sent via + ``Prefer: odata.maxpagesize``. Only used for multi-record queries. + :type page_size: :class:`int` or None + :param count: If ``True``, adds ``$count=true`` to include a total + record count in the response. Only used for multi-record queries. + :type count: :class:`bool` + :param include_annotations: OData annotation pattern for the + ``Prefer: odata.include-annotations`` header (e.g. ``"*"`` or + ``"OData.Community.Display.V1.FormattedValue"``), or ``None``. + Only used for multi-record queries. + :type include_annotations: :class:`str` or None + + :return: A single :class:`~PowerPlatform.Dataverse.models.record.Record` + when ``record_id`` is provided, or a generator yielding pages + (lists of :class:`~PowerPlatform.Dataverse.models.record.Record`) + when fetching multiple records. + :rtype: ~PowerPlatform.Dataverse.models.record.Record or + collections.abc.Iterable[list[~PowerPlatform.Dataverse.models.record.Record]] + + :raises TypeError: If ``record_id`` is provided but not a string. + :raises ValueError: If query parameters are provided alongside + ``record_id``. + + .. rubric:: Example + + Fetch a single record:: + + record = client.records.get( + "account", account_id, select=["name", "telephone1"] + ) + print(record["name"]) + + Fetch multiple records with pagination:: + + for page in client.records.get( + "account", + filter="statecode eq 0", + select=["name", "telephone1"], + page_size=50, + ): + for record in page: + print(record["name"]) + + + + .. py:method:: retrieve(table: str, record_id: str, *, select: Optional[List[str]] = None, expand: Optional[List[str]] = None, include_annotations: Optional[str] = None) -> Optional[PowerPlatform.Dataverse.models.record.Record] + + Fetch a single record by its GUID, returning ``None`` if not found. + + GA replacement for ``records.get(table, record_id)``. Returns ``None`` + instead of raising when the record does not exist (HTTP 404). + + :param table: Schema name of the table (e.g. ``"account"``). + :type table: :class:`str` + :param record_id: GUID of the record to retrieve. + :type record_id: :class:`str` + :param select: Optional list of column logical names to include. + :type select: list[str] or None + :param expand: Optional list of navigation properties to expand (e.g. + ``["primarycontactid"]``). Navigation property names are + case-sensitive and must match the entity's ``$metadata``. + :type expand: list[str] or None + :param include_annotations: OData annotation pattern for the + ``Prefer: odata.include-annotations`` header (e.g. ``"*"`` or + ``"OData.Community.Display.V1.FormattedValue"``), or ``None``. + :type include_annotations: :class:`str` or None + :return: Typed record, or ``None`` if not found. + :rtype: :class:`~PowerPlatform.Dataverse.models.record.Record` or None + + Example:: + + record = client.records.retrieve( + "account", account_id, + select=["name", "statuscode"], + expand=["primarycontactid"], + include_annotations="OData.Community.Display.V1.FormattedValue", + ) + if record is not None: + contact = record.get("primarycontactid") or {} + print(contact.get("fullname")) + + + + .. py:method:: list(table: str, *, filter: Optional[Union[str, PowerPlatform.Dataverse.models.filters.FilterExpression]] = None, select: Optional[List[str]] = None, orderby: Optional[List[str]] = None, top: Optional[int] = None, expand: Optional[List[str]] = None, page_size: Optional[int] = None, count: bool = False, include_annotations: Optional[str] = None) -> PowerPlatform.Dataverse.models.record.QueryResult + + Fetch multiple records and return them as a :class:`~PowerPlatform.Dataverse.models.record.QueryResult`. + + GA replacement for ``records.get(table, filter=...)``. All pages are + collected eagerly and returned as a single :class:`~PowerPlatform.Dataverse.models.record.QueryResult`. + + :param table: Schema name of the table (e.g. ``"account"``). + :type table: :class:`str` + :param filter: Optional OData filter string or :class:`~PowerPlatform.Dataverse.models.filters.FilterExpression`. + :type filter: str or FilterExpression or None + :param select: Optional list of column logical names to include. + :type select: list[str] or None + :param orderby: Optional list of sort expressions (e.g. ``["name asc", "createdon desc"]``). + :type orderby: list[str] or None + :param top: Maximum total number of records to return. + :type top: int or None + :param expand: Optional list of navigation properties to expand. + :type expand: list[str] or None + :param page_size: Per-page size hint via ``Prefer: odata.maxpagesize``. + :type page_size: int or None + :param count: If ``True``, adds ``$count=true`` to include a total record count. + :type count: bool + :param include_annotations: OData annotation pattern for the + ``Prefer: odata.include-annotations`` header, or ``None``. + :type include_annotations: :class:`str` or None + :return: All matching records collected into a :class:`~PowerPlatform.Dataverse.models.record.QueryResult`. + :rtype: :class:`~PowerPlatform.Dataverse.models.record.QueryResult` + + Example:: + + from PowerPlatform.Dataverse import col + + result = client.records.list( + "account", + filter=col("statecode") == 0, + select=["name", "statuscode"], + orderby=["name asc"], + top=100, + include_annotations="OData.Community.Display.V1.FormattedValue", + ) + for record in result: + print(record["name"], record.get("statuscode@OData.Community.Display.V1.FormattedValue")) + + + + .. py:method:: list_pages(table: str, *, filter: Optional[Union[str, PowerPlatform.Dataverse.models.filters.FilterExpression]] = None, select: Optional[List[str]] = None, orderby: Optional[List[str]] = None, top: Optional[int] = None, expand: Optional[List[str]] = None, page_size: Optional[int] = None, count: bool = False, include_annotations: Optional[str] = None) -> Iterator[PowerPlatform.Dataverse.models.record.QueryResult] + + Lazily yield one :class:`~PowerPlatform.Dataverse.models.record.QueryResult` per HTTP page. + + Streaming counterpart to :meth:`list`. Each iteration triggers one + network request via ``@odata.nextLink``. One-shot — do not iterate + more than once. + + :param table: Schema name of the table (e.g. ``"account"``). + :type table: :class:`str` + :param filter: Optional OData filter string or :class:`~PowerPlatform.Dataverse.models.filters.FilterExpression`. + :type filter: str or FilterExpression or None + :param select: Optional list of column logical names to include. + :type select: list[str] or None + :param orderby: Optional list of sort expressions (e.g. ``["name asc", "createdon desc"]``). + :type orderby: list[str] or None + :param top: Maximum total number of records to return. + :type top: int or None + :param expand: Optional list of navigation properties to expand. + :type expand: list[str] or None + :param page_size: Per-page size hint via ``Prefer: odata.maxpagesize``. + :type page_size: int or None + :param count: If ``True``, adds ``$count=true`` to include a total record count. + :type count: bool + :param include_annotations: OData annotation pattern for the + ``Prefer: odata.include-annotations`` header, or ``None``. + :type include_annotations: :class:`str` or None + :return: Iterator of per-page :class:`~PowerPlatform.Dataverse.models.record.QueryResult` objects. + :rtype: Iterator[:class:`~PowerPlatform.Dataverse.models.record.QueryResult`] + + Example:: + + for page in client.records.list_pages( + "account", + filter="statecode eq 0", + orderby=["name asc"], + page_size=200, + ): + process(page.to_dataframe()) + + + + .. py:method:: upsert(table: str, items: List[Union[PowerPlatform.Dataverse.models.upsert.UpsertItem, Dict[str, Any]]]) -> None + + Upsert one or more records identified by alternate keys. + + When ``items`` contains a single entry, performs a single upsert via PATCH + using the alternate key in the URL. When ``items`` contains multiple entries, + uses the ``UpsertMultiple`` bulk action. + + Each item must be either a :class:`~PowerPlatform.Dataverse.models.upsert.UpsertItem` + or a plain ``dict`` with ``"alternate_key"`` and ``"record"`` keys (both dicts). + + :param table: Schema name of the table (e.g. ``"account"`` or ``"new_MyTestTable"``). + :type table: str + :param items: Non-empty list of :class:`~PowerPlatform.Dataverse.models.upsert.UpsertItem` + instances or dicts with ``"alternate_key"`` and ``"record"`` keys. + :type items: list[UpsertItem | dict] + + :return: ``None`` + :rtype: None + + :raises TypeError: If ``items`` is not a non-empty list, or if any element is + neither a :class:`~PowerPlatform.Dataverse.models.upsert.UpsertItem` nor a + dict with ``"alternate_key"`` and ``"record"`` keys. + + .. rubric:: Example + + Upsert a single record using ``UpsertItem``:: + + from PowerPlatform.Dataverse.models import UpsertItem + + client.records.upsert("account", [ + UpsertItem( + alternate_key={"accountnumber": "ACC-001"}, + record={"name": "Contoso Ltd", "description": "Primary account"}, + ) + ]) + + Upsert a single record using a plain dict:: + + client.records.upsert("account", [ + { + "alternate_key": {"accountnumber": "ACC-001"}, + "record": {"name": "Contoso Ltd", "description": "Primary account"}, + }, + ]) + + Upsert multiple records using ``UpsertItem``:: + + from PowerPlatform.Dataverse.models import UpsertItem + + client.records.upsert("account", [ + UpsertItem( + alternate_key={"accountnumber": "ACC-001"}, + record={"name": "Contoso Ltd", "description": "Primary account"}, + ), + UpsertItem( + alternate_key={"accountnumber": "ACC-002"}, + record={"name": "Fabrikam Inc", "description": "Partner account"}, + ), + ]) + + Upsert multiple records using plain dicts:: + + client.records.upsert("account", [ + { + "alternate_key": {"accountnumber": "ACC-001"}, + "record": {"name": "Contoso Ltd", "description": "Primary account"}, + }, + { + "alternate_key": {"accountnumber": "ACC-002"}, + "record": {"name": "Fabrikam Inc", "description": "Partner account"}, + }, + ]) + + The ``alternate_key`` dict may contain multiple columns when the configured + alternate key is composite, e.g. + ``{"accountnumber": "ACC-001", "address1_postalcode": "98052"}``. + + + diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/operations/tables/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/operations/tables/index.rst new file mode 100644 index 00000000..0cf63a2e --- /dev/null +++ b/docs_local/autoapi/PowerPlatform/Dataverse/operations/tables/index.rst @@ -0,0 +1,683 @@ +PowerPlatform.Dataverse.operations.tables +========================================= + +.. py:module:: PowerPlatform.Dataverse.operations.tables + +.. autoapi-nested-parse:: + + Table metadata operations namespace for the Dataverse SDK. + + + +Classes +------- + +.. autoapisummary:: + + PowerPlatform.Dataverse.operations.tables.TableOperations + + +Module Contents +--------------- + +.. py:class:: TableOperations(client: PowerPlatform.Dataverse.client.DataverseClient) + + Namespace for table-level metadata operations. + + Accessed via ``client.tables``. Provides operations to create, delete, + inspect, and list Dataverse tables, as well as add and remove columns. + + :param client: The parent :class:`~PowerPlatform.Dataverse.client.DataverseClient` instance. + :type client: ~PowerPlatform.Dataverse.client.DataverseClient + + Example:: + + client = DataverseClient(base_url, credential) + + # Create a table + info = client.tables.create( + "new_Product", + {"new_Price": "decimal", "new_InStock": "bool"}, + solution="MySolution", + ) + + # List tables + tables = client.tables.list() + + # Get table info + info = client.tables.get("new_Product") + + # Add columns + client.tables.add_columns("new_Product", {"new_Rating": "int"}) + + # Remove columns + client.tables.remove_columns("new_Product", "new_Rating") + + # Delete a table + client.tables.delete("new_Product") + + + .. py:method:: create(table: str, columns: Dict[str, Any], *, solution: Optional[str] = None, primary_column: Optional[str] = None, display_name: Optional[str] = None) -> PowerPlatform.Dataverse.models.table_info.TableInfo + + Create a custom table with the specified columns. + + :param table: Schema name of the table with customization prefix + (e.g. ``"new_MyTestTable"``). + :type table: :class:`str` + :param columns: Mapping of column schema names (with customization + prefix) to their types. Supported types include ``"string"`` + (or ``"text"``), ``"memo"`` (or ``"multiline"``), + ``"int"`` (or ``"integer"``), ``"decimal"`` + (or ``"money"``), ``"float"`` (or ``"double"``), ``"datetime"`` + (or ``"date"``), ``"bool"`` (or ``"boolean"``), ``"file"``, and + ``Enum`` subclasses + (for local option sets). + :type columns: :class:`dict` + :param solution: Optional solution unique name that should own the new + table. When omitted the table is created in the default solution. + :type solution: :class:`str` or None + :param primary_column: Optional primary name column schema name with + customization prefix (e.g. ``"new_ProductName"``). If not provided, + defaults to ``"{prefix}_Name"``. + :type primary_column: :class:`str` or None + :param display_name: Human-readable display name for the table + (e.g. ``"Product"``). When omitted, defaults to the table schema name. + :type display_name: :class:`str` or None + + :return: Table metadata with ``schema_name``, ``entity_set_name``, + ``logical_name``, ``metadata_id``, and ``columns_created``. + Supports dict-like access with legacy keys for backward + compatibility. + :rtype: :class:`~PowerPlatform.Dataverse.models.table_info.TableInfo` + + :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: + If table creation fails or the table already exists. + + .. rubric:: Example + + Create a table with simple columns:: + + from enum import IntEnum + + class ItemStatus(IntEnum): + ACTIVE = 1 + INACTIVE = 2 + + result = client.tables.create( + "new_Product", + { + "new_Title": "string", + "new_Price": "decimal", + "new_Status": ItemStatus, + }, + solution="MySolution", + primary_column="new_ProductName", + display_name="Product", + ) + print(f"Created: {result['table_schema_name']}") + + + + .. py:method:: delete(table: str) -> None + + Delete a custom table by schema name. + + :param table: Schema name of the table (e.g. ``"new_MyTestTable"``). + :type table: :class:`str` + + :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: + If the table does not exist or deletion fails. + + .. warning:: + This operation is irreversible and will delete all records in the + table along with the table definition. + + Example:: + + client.tables.delete("new_MyTestTable") + + + + .. py:method:: get(table: str) -> Optional[PowerPlatform.Dataverse.models.table_info.TableInfo] + + Get basic metadata for a table if it exists. + + :param table: Schema name of the table (e.g. ``"new_MyTestTable"`` + or ``"account"``). + :type table: :class:`str` + + :return: Table metadata, or ``None`` if the table is not found. + Supports dict-like access with legacy keys for backward + compatibility. + :rtype: :class:`~PowerPlatform.Dataverse.models.table_info.TableInfo` + or None + + Example:: + + info = client.tables.get("new_MyTestTable") + if info: + print(f"Logical name: {info['table_logical_name']}") + print(f"Entity set: {info['entity_set_name']}") + + + + .. py:method:: list(*, filter: Optional[str] = None, select: Optional[List[str]] = None) -> List[Dict[str, Any]] + + List all non-private tables in the Dataverse environment. + + By default returns every table where ``IsPrivate eq false``. Supply + an optional OData ``$filter`` expression to further narrow the results. + The expression is combined with the default ``IsPrivate eq false`` + clause using ``and``. + + :param filter: Optional OData ``$filter`` expression to further narrow + the list of returned tables (e.g. + ``"SchemaName eq 'Account'"``). Column names in filter + expressions must use the exact property names from the + ``EntityDefinitions`` metadata (typically PascalCase). + :type filter: :class:`str` or None + :param select: Optional list of property names to include in the + response (projected via the OData ``$select`` query option). + Property names must use the exact PascalCase names from the + ``EntityDefinitions`` metadata (e.g. + ``["LogicalName", "SchemaName", "DisplayName"]``). + When ``None`` (the default) or an empty list, all properties are + returned. + :type select: list[str] or None + + :return: List of EntityDefinition metadata dictionaries. + :rtype: list[dict] + + Example:: + + # List all non-private tables + tables = client.tables.list() + for table in tables: + print(table["LogicalName"]) + + # List only tables whose schema name starts with "new_" + custom_tables = client.tables.list( + filter="startswith(SchemaName, 'new_')" + ) + + # List tables with only specific properties + tables = client.tables.list( + select=["LogicalName", "SchemaName", "EntitySetName"] + ) + + + + .. py:method:: add_columns(table: str, columns: Dict[str, Any]) -> List[str] + + Add one or more columns to an existing table. + + :param table: Schema name of the table (e.g. ``"new_MyTestTable"``). + :type table: :class:`str` + :param columns: Mapping of column schema names (with customization + prefix) to their types. Supported types are the same as for + :meth:`create`. + :type columns: :class:`dict` + + :return: Schema names of the columns that were created. + :rtype: list[str] + + :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: + If the table does not exist. + + Example:: + + created = client.tables.add_columns( + "new_MyTestTable", + {"new_Notes": "string", "new_Active": "bool"}, + ) + print(created) # ['new_Notes', 'new_Active'] + + + + .. py:method:: remove_columns(table: str, columns: Union[str, List[str]]) -> List[str] + + Remove one or more columns from a table. + + :param table: Schema name of the table (e.g. ``"new_MyTestTable"``). + :type table: :class:`str` + :param columns: Column schema name or list of column schema names to + remove. Must include the customization prefix (e.g. + ``"new_TestColumn"``). + :type columns: str or list[str] + + :return: Schema names of the columns that were removed. + :rtype: list[str] + + :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: + If the table or a specified column does not exist. + + Example:: + + removed = client.tables.remove_columns( + "new_MyTestTable", + ["new_Notes", "new_Active"], + ) + print(removed) # ['new_Notes', 'new_Active'] + + + + .. py:method:: create_one_to_many_relationship(lookup: PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata, relationship: PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata, *, solution: Optional[str] = None) -> PowerPlatform.Dataverse.models.relationship.RelationshipInfo + + Create a one-to-many relationship between tables. + + This operation creates both the relationship and the lookup attribute + on the referencing table. + + :param lookup: Metadata defining the lookup attribute. + :type lookup: ~PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata + :param relationship: Metadata defining the relationship. + :type relationship: ~PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata + :param solution: Optional solution unique name to add relationship to. + :type solution: :class:`str` or None + + :return: Relationship metadata with ``relationship_id``, + ``relationship_schema_name``, ``relationship_type``, + ``lookup_schema_name``, ``referenced_entity``, and + ``referencing_entity``. + :rtype: :class:`~PowerPlatform.Dataverse.models.relationship.RelationshipInfo` + + :raises ~PowerPlatform.Dataverse.core.errors.HttpError: + If the Web API request fails. + + .. rubric:: Example + + Create a one-to-many relationship: Department (1) -> Employee (N):: + + from PowerPlatform.Dataverse.models import ( + LookupAttributeMetadata, + OneToManyRelationshipMetadata, + Label, + LocalizedLabel, + CascadeConfiguration, + ) + from PowerPlatform.Dataverse.common.constants import ( + CASCADE_BEHAVIOR_REMOVE_LINK, + ) + + lookup = LookupAttributeMetadata( + schema_name="new_DepartmentId", + display_name=Label( + localized_labels=[ + LocalizedLabel(label="Department", language_code=1033) + ] + ), + ) + + relationship = OneToManyRelationshipMetadata( + schema_name="new_Department_Employee", + referenced_entity="new_department", + referencing_entity="new_employee", + referenced_attribute="new_departmentid", + cascade_configuration=CascadeConfiguration( + delete=CASCADE_BEHAVIOR_REMOVE_LINK, + ), + ) + + result = client.tables.create_one_to_many_relationship(lookup, relationship) + print(f"Created lookup field: {result.lookup_schema_name}") + + + + .. py:method:: create_many_to_many_relationship(relationship: PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata, *, solution: Optional[str] = None) -> PowerPlatform.Dataverse.models.relationship.RelationshipInfo + + Create a many-to-many relationship between tables. + + This operation creates a many-to-many relationship and an intersect + table to manage the relationship. + + :param relationship: Metadata defining the many-to-many relationship. + :type relationship: ~PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata + :param solution: Optional solution unique name to add relationship to. + :type solution: :class:`str` or None + + :return: Relationship metadata with ``relationship_id``, + ``relationship_schema_name``, ``relationship_type``, + ``entity1_logical_name``, and ``entity2_logical_name``. + :rtype: :class:`~PowerPlatform.Dataverse.models.relationship.RelationshipInfo` + + :raises ~PowerPlatform.Dataverse.core.errors.HttpError: + If the Web API request fails. + + .. rubric:: Example + + Create a many-to-many relationship: Employee <-> Project:: + + from PowerPlatform.Dataverse.models import ( + ManyToManyRelationshipMetadata, + ) + + relationship = ManyToManyRelationshipMetadata( + schema_name="new_employee_project", + entity1_logical_name="new_employee", + entity2_logical_name="new_project", + ) + + result = client.tables.create_many_to_many_relationship(relationship) + print(f"Created: {result.relationship_schema_name}") + + + + .. py:method:: delete_relationship(relationship_id: str) -> None + + Delete a relationship by its metadata ID. + + :param relationship_id: The GUID of the relationship metadata. + :type relationship_id: :class:`str` + + :raises ~PowerPlatform.Dataverse.core.errors.HttpError: + If the Web API request fails. + + .. warning:: + Deleting a relationship also removes the associated lookup attribute + for one-to-many relationships. This operation is irreversible. + + Example:: + + client.tables.delete_relationship( + "12345678-1234-1234-1234-123456789abc" + ) + + + + .. py:method:: get_relationship(schema_name: str) -> Optional[PowerPlatform.Dataverse.models.relationship.RelationshipInfo] + + Retrieve relationship metadata by schema name. + + :param schema_name: The schema name of the relationship. + :type schema_name: :class:`str` + + :return: Relationship metadata, or ``None`` if not found. + :rtype: :class:`~PowerPlatform.Dataverse.models.relationship.RelationshipInfo` + or None + + :raises ~PowerPlatform.Dataverse.core.errors.HttpError: + If the Web API request fails. + + Example:: + + rel = client.tables.get_relationship("new_Department_Employee") + if rel: + print(f"Found: {rel.relationship_schema_name}") + + + + .. py:method:: create_lookup_field(referencing_table: str, lookup_field_name: str, referenced_table: str, *, display_name: Optional[str] = None, description: Optional[str] = None, required: bool = False, cascade_delete: str = CASCADE_BEHAVIOR_REMOVE_LINK, solution: Optional[str] = None, language_code: int = 1033) -> PowerPlatform.Dataverse.models.relationship.RelationshipInfo + + Create a simple lookup field relationship. + + This is a convenience method that wraps :meth:`create_one_to_many_relationship` + for the common case of adding a lookup field to an existing table. + + :param referencing_table: Logical name of the table that will have + the lookup field (child table). + :type referencing_table: :class:`str` + :param lookup_field_name: Schema name for the lookup field + (e.g., ``"new_AccountId"``). + :type lookup_field_name: :class:`str` + :param referenced_table: Logical name of the table being referenced + (parent table). + :type referenced_table: :class:`str` + :param display_name: Display name for the lookup field. Defaults to + the referenced table name. + :type display_name: :class:`str` or None + :param description: Optional description for the lookup field. + :type description: :class:`str` or None + :param required: Whether the lookup is required. Defaults to ``False``. + :type required: :class:`bool` + :param cascade_delete: Delete behavior (``"RemoveLink"``, + ``"Cascade"``, ``"Restrict"``). Defaults to ``"RemoveLink"``. + :type cascade_delete: :class:`str` + :param solution: Optional solution unique name to add the relationship + to. + :type solution: :class:`str` or None + :param language_code: Language code for labels. Defaults to 1033 + (English). + :type language_code: :class:`int` + + :return: Relationship metadata with ``relationship_id``, + ``relationship_schema_name``, ``relationship_type``, + ``lookup_schema_name``, ``referenced_entity``, and + ``referencing_entity``. + :rtype: :class:`~PowerPlatform.Dataverse.models.relationship.RelationshipInfo` + + :raises ~PowerPlatform.Dataverse.core.errors.HttpError: + If the Web API request fails. + + .. rubric:: Example + + Create a simple lookup field:: + + result = client.tables.create_lookup_field( + referencing_table="new_order", + lookup_field_name="new_AccountId", + referenced_table="account", + display_name="Account", + required=True, + cascade_delete=CASCADE_BEHAVIOR_REMOVE_LINK, + ) + print(f"Created lookup: {result['lookup_schema_name']}") + + + + .. py:method:: create_alternate_key(table: str, key_name: str, columns: List[str], *, display_name: Optional[str] = None, language_code: int = 1033) -> PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo + + Create an alternate key on a table. + + Alternate keys allow upsert operations to identify records by one or + more columns instead of the primary GUID. After creation the key is + queued for index building; its :attr:`~PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo.status` will + transition from ``"Pending"`` to ``"Active"`` once the index is ready. + + :param table: Schema name of the table (e.g. ``"new_Product"``). + :type table: :class:`str` + :param key_name: Schema name for the new alternate key + (e.g. ``"new_product_code_key"``). + :type key_name: :class:`str` + :param columns: List of column logical names that compose the key + (e.g. ``["new_productcode"]``). + :type columns: list[str] + :param display_name: Display name for the key. Defaults to + ``key_name`` if not provided. + :type display_name: :class:`str` or None + :param language_code: Language code for labels. Defaults to 1033 + (English). + :type language_code: :class:`int` + + :return: Metadata for the newly created alternate key. + :rtype: :class:`~PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo` + + :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: + If the table does not exist. + :raises ~PowerPlatform.Dataverse.core.errors.HttpError: + If the Web API request fails. + + .. rubric:: Example + + Create a single-column alternate key for upsert:: + + key = client.tables.create_alternate_key( + "new_Product", + "new_product_code_key", + ["new_productcode"], + display_name="Product Code", + ) + print(f"Key ID: {key.metadata_id}") + print(f"Columns: {key.key_attributes}") + + + + .. py:method:: get_alternate_keys(table: str) -> List[PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo] + + List all alternate keys defined on a table. + + :param table: Schema name of the table (e.g. ``"new_Product"``). + :type table: :class:`str` + + :return: List of alternate key metadata objects. May be empty if no + alternate keys are defined. + :rtype: list[~PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo] + + :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: + If the table does not exist. + :raises ~PowerPlatform.Dataverse.core.errors.HttpError: + If the Web API request fails. + + .. rubric:: Example + + List alternate keys and print their status:: + + keys = client.tables.get_alternate_keys("new_Product") + for key in keys: + print(f"{key.schema_name}: {key.status}") + + + + .. py:method:: delete_alternate_key(table: str, key_id: str) -> None + + Delete an alternate key by its metadata ID. + + :param table: Schema name of the table (e.g. ``"new_Product"``). + :type table: :class:`str` + :param key_id: Metadata GUID of the alternate key to delete. + :type key_id: :class:`str` + + :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: + If the table does not exist. + :raises ~PowerPlatform.Dataverse.core.errors.HttpError: + If the Web API request fails. + + .. warning:: + Deleting an alternate key that is in use by upsert operations will + cause those operations to fail. This operation is irreversible. + + Example:: + + client.tables.delete_alternate_key( + "new_Product", + "12345678-1234-1234-1234-123456789abc", + ) + + + + .. py:method:: list_columns(table: str, *, select: Optional[List[str]] = None, filter: Optional[str] = None) -> List[Dict[str, Any]] + + List all attribute (column) definitions for a table. + + :param table: Schema name of the table (e.g. ``"account"`` or + ``"new_Product"``). + :type table: :class:`str` + :param select: Optional list of property names to project via + ``$select``. Values are passed as-is (PascalCase). + :type select: list[str] or None + :param filter: Optional OData ``$filter`` expression. For example, + ``"AttributeType eq 'String'"`` returns only string columns. + :type filter: :class:`str` or None + + :return: List of raw attribute metadata dictionaries. + :rtype: list[dict[str, typing.Any]] + + :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: + If the table is not found. + :raises ~PowerPlatform.Dataverse.core.errors.HttpError: + If the Web API request fails. + + Example:: + + # List all columns on the account table + columns = client.tables.list_columns("account") + for col in columns: + print(f"{col['LogicalName']} ({col.get('AttributeType')})") + + # List only specific properties + columns = client.tables.list_columns( + "account", + select=["LogicalName", "SchemaName", "AttributeType"], + ) + + # Filter to only string attributes + columns = client.tables.list_columns( + "account", + filter="AttributeType eq 'String'", + ) + + + + .. py:method:: list_relationships(*, filter: Optional[str] = None, select: Optional[List[str]] = None) -> List[Dict[str, Any]] + + List all relationship definitions in the environment. + + :param filter: Optional OData ``$filter`` expression. For example, + ``"RelationshipType eq Microsoft.Dynamics.CRM.RelationshipType'OneToManyRelationship'"`` + returns only one-to-many relationships. + :type filter: :class:`str` or None + :param select: Optional list of property names to project via + ``$select``. Values are passed as-is (PascalCase). + :type select: list[str] or None + + :return: List of raw relationship metadata dictionaries. + :rtype: list[dict[str, typing.Any]] + + :raises ~PowerPlatform.Dataverse.core.errors.HttpError: + If the Web API request fails. + + Example:: + + # List all relationships + rels = client.tables.list_relationships() + for rel in rels: + print(f"{rel['SchemaName']} ({rel.get('@odata.type')})") + + # Filter by type + one_to_many = client.tables.list_relationships( + filter="RelationshipType eq Microsoft.Dynamics.CRM.RelationshipType'OneToManyRelationship'" + ) + + # Select specific properties + rels = client.tables.list_relationships( + select=["SchemaName", "ReferencedEntity", "ReferencingEntity"] + ) + + + + .. py:method:: list_table_relationships(table: str, *, filter: Optional[str] = None, select: Optional[List[str]] = None) -> List[Dict[str, Any]] + + List all relationships for a specific table. + + Combines one-to-many, many-to-one, and many-to-many relationships + for the given table by querying + ``EntityDefinitions({id})/OneToManyRelationships``, + ``EntityDefinitions({id})/ManyToOneRelationships``, and + ``EntityDefinitions({id})/ManyToManyRelationships``. + + :param table: Schema name of the table (e.g. ``"account"``). + :type table: :class:`str` + :param filter: Optional OData ``$filter`` expression applied to each + sub-request. + :type filter: :class:`str` or None + :param select: Optional list of property names to project via + ``$select``. Values are passed as-is (PascalCase). + :type select: list[str] or None + + :return: Combined list of one-to-many, many-to-one, and many-to-many + relationship metadata dictionaries. + :rtype: list[dict[str, typing.Any]] + + :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: + If the table is not found. + :raises ~PowerPlatform.Dataverse.core.errors.HttpError: + If the Web API request fails. + + Example:: + + # List all relationships for the account table + rels = client.tables.list_table_relationships("account") + for rel in rels: + print(f"{rel['SchemaName']} -> {rel.get('@odata.type')}") + + + diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/utils/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/utils/index.rst new file mode 100644 index 00000000..bf579628 --- /dev/null +++ b/docs_local/autoapi/PowerPlatform/Dataverse/utils/index.rst @@ -0,0 +1,13 @@ +PowerPlatform.Dataverse.utils +============================= + +.. py:module:: PowerPlatform.Dataverse.utils + +.. autoapi-nested-parse:: + + Utilities and adapters for the Dataverse SDK. + + Placeholder module for future utility adapters. + + + diff --git a/docs_local/autoapi/PowerPlatform/index.rst b/docs_local/autoapi/PowerPlatform/index.rst new file mode 100644 index 00000000..6bf6b5a5 --- /dev/null +++ b/docs_local/autoapi/PowerPlatform/index.rst @@ -0,0 +1,15 @@ +PowerPlatform +============= + +.. py:module:: PowerPlatform + + +Submodules +---------- + +.. toctree:: + :maxdepth: 1 + + /autoapi/PowerPlatform/Dataverse/index + + diff --git a/docs_local/autoapi/index.rst b/docs_local/autoapi/index.rst new file mode 100644 index 00000000..79277872 --- /dev/null +++ b/docs_local/autoapi/index.rst @@ -0,0 +1,11 @@ +API Reference +============= + +This page contains auto-generated API reference documentation [#f1]_. + +.. toctree:: + :titlesonly: + + /autoapi/PowerPlatform/index + +.. [#f1] Created with `sphinx-autoapi `_ \ No newline at end of file diff --git a/docs_local/build.log b/docs_local/build.log new file mode 100644 index 00000000..9d85d230 --- /dev/null +++ b/docs_local/build.log @@ -0,0 +1,216 @@ +Running Sphinx v9.1.0 +loading translations [en]... done +making output directory... done +[AutoAPI] Reading files... [ 2%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\__init__.py [AutoAPI] Reading files... [ 5%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\client.py [AutoAPI] Reading files... [ 7%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\_skill_installer.py [AutoAPI] Reading files... [ 9%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\__init__.py [AutoAPI] Reading files... [ 11%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\claude_skill\__init__.py [AutoAPI] Reading files... [ 14%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\constants.py [AutoAPI] Reading files... [ 16%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\__init__.py [AutoAPI] Reading files... [ 18%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\config.py [AutoAPI] Reading files... [ 20%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\errors.py [AutoAPI] Reading files... [ 23%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\log_config.py [AutoAPI] Reading files... [ 25%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_auth.py [AutoAPI] Reading files... [ 27%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_error_codes.py [AutoAPI] Reading files... [ 30%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http.py [AutoAPI] Reading files... [ 32%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http_logger.py [AutoAPI] Reading files... [ 34%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\__init__.py [AutoAPI] Reading files... [ 36%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch.py [AutoAPI] Reading files... [ 39%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata.py [AutoAPI] Reading files... [ 41%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_raw_request.py [AutoAPI] Reading files... [ 43%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_relationships.py [AutoAPI] Reading files... [ 45%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_upload.py [AutoAPI] Reading files... [ 48%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\__init__.py [AutoAPI] Reading files... [ 50%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\extensions\__init__.py [AutoAPI] Reading files... [ 52%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\migrate_v0_to_v1.py [AutoAPI] Reading files... [ 55%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\__init__.py [AutoAPI] Reading files... [ 57%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\batch.py [AutoAPI] Reading files... [ 59%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\fetchxml_query.py [AutoAPI] Reading files... [ 61%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\filters.py [AutoAPI] Reading files... [ 64%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\labels.py [AutoAPI] Reading files... [ 66%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\protocol.py [AutoAPI] Reading files... [ 68%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\query_builder.py [AutoAPI] Reading files... [ 70%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\record.py [AutoAPI] Reading files... [ 73%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\relationship.py [AutoAPI] Reading files... [ 75%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\table_info.py [AutoAPI] Reading files... [ 77%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\upsert.py [AutoAPI] Reading files... [ 80%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\__init__.py [AutoAPI] Reading files... [ 82%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\batch.py [AutoAPI] Reading files... [ 84%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\dataframe.py [AutoAPI] Reading files... [ 86%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\files.py [AutoAPI] Reading files... [ 89%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\query.py [AutoAPI] Reading files... [ 91%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\records.py [AutoAPI] Reading files... [ 93%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\tables.py [AutoAPI] Reading files... [ 95%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\__init__.py [AutoAPI] Reading files... [ 98%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\_pandas.py [AutoAPI] Reading files... [100%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\__init__.py +[AutoAPI] Mapping Data... [ 2%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\__init__.py [AutoAPI] Mapping Data... [ 5%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\client.py [AutoAPI] Mapping Data... [ 7%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\_skill_installer.py [AutoAPI] Mapping Data... [ 9%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\__init__.py [AutoAPI] Mapping Data... [ 11%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\claude_skill\__init__.py [AutoAPI] Mapping Data... [ 14%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\constants.py [AutoAPI] Mapping Data... [ 16%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\__init__.py [AutoAPI] Mapping Data... [ 18%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\config.py [AutoAPI] Mapping Data... [ 20%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\errors.py [AutoAPI] Mapping Data... [ 23%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\log_config.py [AutoAPI] Mapping Data... [ 25%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_auth.py [AutoAPI] Mapping Data... [ 27%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_error_codes.py [AutoAPI] Mapping Data... [ 30%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http.py [AutoAPI] Mapping Data... [ 32%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http_logger.py [AutoAPI] Mapping Data... [ 34%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\__init__.py [AutoAPI] Mapping Data... [ 36%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch.py [AutoAPI] Mapping Data... [ 39%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata.py [AutoAPI] Mapping Data... [ 41%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_raw_request.py [AutoAPI] Mapping Data... [ 43%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_relationships.py [AutoAPI] Mapping Data... [ 45%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_upload.py [AutoAPI] Mapping Data... [ 48%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\__init__.py [AutoAPI] Mapping Data... [ 50%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\extensions\__init__.py [AutoAPI] Mapping Data... [ 52%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\migrate_v0_to_v1.py [AutoAPI] Mapping Data... [ 55%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\__init__.py [AutoAPI] Mapping Data... [ 57%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\batch.py [AutoAPI] Mapping Data... [ 59%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\fetchxml_query.py [AutoAPI] Mapping Data... [ 61%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\filters.py [AutoAPI] Mapping Data... [ 64%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\labels.py [AutoAPI] Mapping Data... [ 66%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\protocol.py [AutoAPI] Mapping Data... [ 68%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\query_builder.py [AutoAPI] Mapping Data... [ 70%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\record.py [AutoAPI] Mapping Data... [ 73%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\relationship.py [AutoAPI] Mapping Data... [ 75%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\table_info.py [AutoAPI] Mapping Data... [ 77%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\upsert.py [AutoAPI] Mapping Data... [ 80%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\__init__.py [AutoAPI] Mapping Data... [ 82%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\batch.py [AutoAPI] Mapping Data... [ 84%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\dataframe.py [AutoAPI] Mapping Data... [ 86%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\files.py [AutoAPI] Mapping Data... [ 89%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\query.py [AutoAPI] Mapping Data... [ 91%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\records.py [AutoAPI] Mapping Data... [ 93%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\tables.py [AutoAPI] Mapping Data... [ 95%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\__init__.py [AutoAPI] Mapping Data... [ 98%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\_pandas.py [AutoAPI] Mapping Data... [100%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\__init__.py +[AutoAPI] Rendering Data... [ 3%] PowerPlatform +[AutoAPI] Rendering Data... [ 6%] PowerPlatform.Dataverse +[AutoAPI] Rendering Data... [ 9%] PowerPlatform.Dataverse.core +[AutoAPI] Rendering Data... [ 12%] PowerPlatform.Dataverse.data +[AutoAPI] Rendering Data... [ 15%] PowerPlatform.Dataverse.utils +[AutoAPI] Rendering Data... [ 18%] PowerPlatform.Dataverse.client +[AutoAPI] Rendering Data... [ 21%] PowerPlatform.Dataverse.common +[AutoAPI] Rendering Data... [ 24%] PowerPlatform.Dataverse.models +[AutoAPI] Rendering Data... [ 27%] PowerPlatform.Dataverse.migration +[AutoAPI] Rendering Data... [ 30%] PowerPlatform.Dataverse.extensions +[AutoAPI] Rendering Data... [ 33%] PowerPlatform.Dataverse.operations +[AutoAPI] Rendering Data... [ 36%] PowerPlatform.Dataverse.core.config +[AutoAPI] Rendering Data... [ 39%] PowerPlatform.Dataverse.core.errors +[AutoAPI] Rendering Data... [ 42%] PowerPlatform.Dataverse.claude_skill +[AutoAPI] Rendering Data... [ 45%] PowerPlatform.Dataverse.models.batch +[AutoAPI] Rendering Data... [ 48%] PowerPlatform.Dataverse.models.labels +[AutoAPI] Rendering Data... [ 52%] PowerPlatform.Dataverse.models.record +[AutoAPI] Rendering Data... [ 55%] PowerPlatform.Dataverse.models.upsert +[AutoAPI] Rendering Data... [ 58%] PowerPlatform.Dataverse.models.filters +[AutoAPI] Rendering Data... [ 61%] PowerPlatform.Dataverse.core.log_config +[AutoAPI] Rendering Data... [ 64%] PowerPlatform.Dataverse.models.protocol +[AutoAPI] Rendering Data... [ 67%] PowerPlatform.Dataverse.common.constants +[AutoAPI] Rendering Data... [ 70%] PowerPlatform.Dataverse.operations.batch +[AutoAPI] Rendering Data... [ 73%] PowerPlatform.Dataverse.operations.files +[AutoAPI] Rendering Data... [ 76%] PowerPlatform.Dataverse.operations.query +[AutoAPI] Rendering Data... [ 79%] PowerPlatform.Dataverse.models.table_info +[AutoAPI] Rendering Data... [ 82%] PowerPlatform.Dataverse.operations.tables +[AutoAPI] Rendering Data... [ 85%] PowerPlatform.Dataverse.operations.records +[AutoAPI] Rendering Data... [ 88%] PowerPlatform.Dataverse.models.relationship +[AutoAPI] Rendering Data... [ 91%] PowerPlatform.Dataverse.models.query_builder +[AutoAPI] Rendering Data... [ 94%] PowerPlatform.Dataverse.operations.dataframe +[AutoAPI] Rendering Data... [ 97%] PowerPlatform.Dataverse.models.fetchxml_query +[AutoAPI] Rendering Data... [100%] PowerPlatform.Dataverse.migration.migrate_v0_to_v1 + +[autosummary] generating autosummary for: index.rst +building [mo]: targets for 0 po files that are out of date +writing output...  +building [html]: targets for 1 source files that are out of date +updating environment: [new config] 35 added, 0 changed, 0 removed +reading sources... [ 3%] autoapi/PowerPlatform/Dataverse/claude_skill/index reading sources... [ 6%] autoapi/PowerPlatform/Dataverse/client/index reading sources... [ 9%] autoapi/PowerPlatform/Dataverse/common/constants/index reading sources... [ 11%] autoapi/PowerPlatform/Dataverse/common/index reading sources... [ 14%] autoapi/PowerPlatform/Dataverse/core/config/index reading sources... [ 17%] autoapi/PowerPlatform/Dataverse/core/errors/index reading sources... [ 20%] autoapi/PowerPlatform/Dataverse/core/index reading sources... [ 23%] autoapi/PowerPlatform/Dataverse/core/log_config/index reading sources... [ 26%] autoapi/PowerPlatform/Dataverse/data/index reading sources... [ 29%] autoapi/PowerPlatform/Dataverse/extensions/index reading sources... [ 31%] autoapi/PowerPlatform/Dataverse/index reading sources... [ 34%] autoapi/PowerPlatform/Dataverse/migration/index reading sources... [ 37%] autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index reading sources... [ 40%] autoapi/PowerPlatform/Dataverse/models/batch/index reading sources... [ 43%] autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index reading sources... [ 46%] autoapi/PowerPlatform/Dataverse/models/filters/index reading sources... [ 49%] autoapi/PowerPlatform/Dataverse/models/index reading sources... [ 51%] autoapi/PowerPlatform/Dataverse/models/labels/index reading sources... [ 54%] autoapi/PowerPlatform/Dataverse/models/protocol/index reading sources... [ 57%] autoapi/PowerPlatform/Dataverse/models/query_builder/index reading sources... [ 60%] autoapi/PowerPlatform/Dataverse/models/record/index reading sources... [ 63%] autoapi/PowerPlatform/Dataverse/models/relationship/index reading sources... [ 66%] autoapi/PowerPlatform/Dataverse/models/table_info/index reading sources... [ 69%] autoapi/PowerPlatform/Dataverse/models/upsert/index reading sources... [ 71%] autoapi/PowerPlatform/Dataverse/operations/batch/index reading sources... [ 74%] autoapi/PowerPlatform/Dataverse/operations/dataframe/index reading sources... [ 77%] autoapi/PowerPlatform/Dataverse/operations/files/index reading sources... [ 80%] autoapi/PowerPlatform/Dataverse/operations/index reading sources... [ 83%] autoapi/PowerPlatform/Dataverse/operations/query/index reading sources... [ 86%] autoapi/PowerPlatform/Dataverse/operations/records/index reading sources... [ 89%] autoapi/PowerPlatform/Dataverse/operations/tables/index reading sources... [ 91%] autoapi/PowerPlatform/Dataverse/utils/index reading sources... [ 94%] autoapi/PowerPlatform/index reading sources... [ 97%] autoapi/index reading sources... [100%] index +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:77: ERROR: Unexpected indentation. [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:72: WARNING: Block quote ends without a blank line; unexpected unindent. [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:78: WARNING: Definition list ends without a blank line; unexpected unindent. [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\filters\index.rst:181: ERROR: Malformed table. +Right border not aligned or missing. + ++-----------------+-----------------------------+-------------------------------------+ +| Pattern form | Example | Compiles to | ++=================+=============================+=====================================+ +| ``val%`` | ``like("Contoso%")`` | ``startswith(column,'Contoso')`` | ++-----------------+-----------------------------+-------------------------------------+ +| ``%val`` | ``like("%Ltd")`` | ``endswith(column,'Ltd')`` | ++-----------------+-----------------------------+-------------------------------------+ +| ``%val%`` | ``like("%Corp%")`` | ``contains(column,'Corp')`` | ++-----------------+-----------------------------+-------------------------------------+ +| No wildcard | ``like("Contoso")`` | ``column eq 'Contoso'`` | ++-----------------+-----------------------------+-------------------------------------+ +| Other | ``like("Con%oso")`` | :class:`ValueError` | ++-----------------+-----------------------------+-------------------------------------+ [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\index.rst:354: ERROR: Malformed table. +Right border not aligned or missing. + ++-----------------+-----------------------------+-------------------------------------+ +| Pattern form | Example | Compiles to | ++=================+=============================+=====================================+ +| ``val%`` | ``like("Contoso%")`` | ``startswith(column,'Contoso')`` | ++-----------------+-----------------------------+-------------------------------------+ +| ``%val`` | ``like("%Ltd")`` | ``endswith(column,'Ltd')`` | ++-----------------+-----------------------------+-------------------------------------+ +| ``%val%`` | ``like("%Corp%")`` | ``contains(column,'Corp')`` | ++-----------------+-----------------------------+-------------------------------------+ +| No wildcard | ``like("Contoso")`` | ``column eq 'Contoso'`` | ++-----------------+-----------------------------+-------------------------------------+ +| Other | ``like("Con%oso")`` | :class:`ValueError` | ++-----------------+-----------------------------+-------------------------------------+ [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:1189: WARNING: Inline emphasis start-string without end-string. [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\query\index.rst:114: WARNING: Inline emphasis start-string without end-string. [docutils] +looking for now-outdated files... none found +pickling environment... done +checking consistency... done +preparing documents... done +copying assets...  +copying static files...  +Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\basic.css +Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\documentation_options.js +Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\language_data.js +Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\alabaster.css +copying static files: done +copying extra files...  +copying extra files: done +copying assets: done +writing output... [ 3%] autoapi/PowerPlatform/Dataverse/claude_skill/index writing output... [ 6%] autoapi/PowerPlatform/Dataverse/client/index writing output... [ 9%] autoapi/PowerPlatform/Dataverse/common/constants/index writing output... [ 11%] autoapi/PowerPlatform/Dataverse/common/index writing output... [ 14%] autoapi/PowerPlatform/Dataverse/core/config/index writing output... [ 17%] autoapi/PowerPlatform/Dataverse/core/errors/index writing output... [ 20%] autoapi/PowerPlatform/Dataverse/core/index writing output... [ 23%] autoapi/PowerPlatform/Dataverse/core/log_config/index writing output... [ 26%] autoapi/PowerPlatform/Dataverse/data/index writing output... [ 29%] autoapi/PowerPlatform/Dataverse/extensions/index writing output... [ 31%] autoapi/PowerPlatform/Dataverse/index writing output... [ 34%] autoapi/PowerPlatform/Dataverse/migration/index writing output... [ 37%] autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index writing output... [ 40%] autoapi/PowerPlatform/Dataverse/models/batch/index writing output... [ 43%] autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index writing output... [ 46%] autoapi/PowerPlatform/Dataverse/models/filters/index writing output... [ 49%] autoapi/PowerPlatform/Dataverse/models/index writing output... [ 51%] autoapi/PowerPlatform/Dataverse/models/labels/index writing output... [ 54%] autoapi/PowerPlatform/Dataverse/models/protocol/index writing output... [ 57%] autoapi/PowerPlatform/Dataverse/models/query_builder/index writing output... [ 60%] autoapi/PowerPlatform/Dataverse/models/record/index writing output... [ 63%] autoapi/PowerPlatform/Dataverse/models/relationship/index writing output... [ 66%] autoapi/PowerPlatform/Dataverse/models/table_info/index writing output... [ 69%] autoapi/PowerPlatform/Dataverse/models/upsert/index writing output... [ 71%] autoapi/PowerPlatform/Dataverse/operations/batch/index writing output... [ 74%] autoapi/PowerPlatform/Dataverse/operations/dataframe/index writing output... [ 77%] autoapi/PowerPlatform/Dataverse/operations/files/index writing output... [ 80%] autoapi/PowerPlatform/Dataverse/operations/index writing output... [ 83%] autoapi/PowerPlatform/Dataverse/operations/query/index writing output... [ 86%] autoapi/PowerPlatform/Dataverse/operations/records/index writing output... [ 89%] autoapi/PowerPlatform/Dataverse/operations/tables/index writing output... [ 91%] autoapi/PowerPlatform/Dataverse/utils/index writing output... [ 94%] autoapi/PowerPlatform/index writing output... [ 97%] autoapi/index writing output... [100%] index +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\client\index.rst:18: WARNING: py:class reference target not found: azure.core.credentials.TokenCredential [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\client\index.rst:22: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._odata._ODataClient [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\client\index.rst:16: WARNING: py:class reference target not found: azure.core.credentials.TokenCredential [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:33::1: WARNING: py:class reference target not found: Record [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:42::1: WARNING: py:class reference target not found: ColumnProxy [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:46: WARNING: more than one target found for cross-reference 'ColumnProxy': PowerPlatform.Dataverse.models.filters.ColumnProxy, PowerPlatform.Dataverse.models.ColumnProxy [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:48: WARNING: py:class reference target not found: ColumnProxy [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:61: WARNING: py:class reference target not found: ColumnProxy [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:65: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:73: WARNING: py:class reference target not found: FilterExpression [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:152: WARNING: more than one target found for cross-reference 'Record': PowerPlatform.Dataverse.models.Record, PowerPlatform.Dataverse.models.record.Record [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:154: WARNING: py:class reference target not found: Record [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:162: WARNING: py:class reference target not found: Record [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:165: WARNING: more than one target found for cross-reference 'Record': PowerPlatform.Dataverse.models.Record, PowerPlatform.Dataverse.models.record.Record [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:169: WARNING: more than one target found for cross-reference 'Record': PowerPlatform.Dataverse.models.Record, PowerPlatform.Dataverse.models.record.Record [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:44: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:113: WARNING: py:class reference target not found: pathlib.Path [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\fetchxml_query\index.rst:37: WARNING: py:class reference target not found: QueryResult [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\fetchxml_query\index.rst:55: WARNING: py:class reference target not found: QueryResult [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\fetchxml_query\index.rst:67: WARNING: py:class reference target not found: QueryResult [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\index.rst:965: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\index.rst:81: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.QueryResult, PowerPlatform.Dataverse.models.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.QueryResult, PowerPlatform.Dataverse.models.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.QueryResult, PowerPlatform.Dataverse.models.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:487: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\record\index.rst:22: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\relationship\index.rst:25: WARNING: more than one target found for cross-reference 'Label': PowerPlatform.Dataverse.models.Label, PowerPlatform.Dataverse.models.labels.Label [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\relationship\index.rst:25: WARNING: more than one target found for cross-reference 'Label': PowerPlatform.Dataverse.models.Label, PowerPlatform.Dataverse.models.labels.Label [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:30: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:93: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:275: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:290: WARNING: py:class reference target not found: FilterExpression [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:413: WARNING: py:attr reference target not found: BatchResult.responses [ref.attr] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:427: WARNING: py:attr reference target not found: BatchResult.responses [ref.attr] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:563: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:585: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:617: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:28: WARNING: more than one target found for cross-reference 'ValidationError': PowerPlatform.Dataverse.core.errors.ValidationError, PowerPlatform.Dataverse.core.ValidationError [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:28: WARNING: more than one target found for cross-reference 'MetadataError': PowerPlatform.Dataverse.core.errors.MetadataError, PowerPlatform.Dataverse.core.MetadataError [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:28: WARNING: more than one target found for cross-reference 'HttpError': PowerPlatform.Dataverse.core.errors.HttpError, PowerPlatform.Dataverse.core.HttpError [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:54: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:93: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:154: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:154: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:189: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:248: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:76: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:98: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:130: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:368: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:383: WARNING: py:class reference target not found: FilterExpression [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:50: WARNING: more than one target found for cross-reference 'ValidationError': PowerPlatform.Dataverse.core.errors.ValidationError, PowerPlatform.Dataverse.core.ValidationError [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:50: WARNING: more than one target found for cross-reference 'MetadataError': PowerPlatform.Dataverse.core.errors.MetadataError, PowerPlatform.Dataverse.core.MetadataError [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:50: WARNING: more than one target found for cross-reference 'HttpError': PowerPlatform.Dataverse.core.errors.HttpError, PowerPlatform.Dataverse.core.HttpError [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:585: WARNING: py:attr reference target not found: BatchResult.responses [ref.attr] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:599: WARNING: py:attr reference target not found: BatchResult.responses [ref.attr] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:680: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:701: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:795: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:834: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:895: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:895: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:930: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:989: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: collections.abc.Iterable [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:1694: WARNING: py:class reference target not found: QueryResult [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:1696: WARNING: py:class reference target not found: QueryResult [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:1701: WARNING: py:class reference target not found: FilterExpression [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:1718: WARNING: py:class reference target not found: QueryResult [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:1740: WARNING: py:class reference target not found: QueryResult [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:1748: WARNING: py:class reference target not found: FilterExpression [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:1765: WARNING: py:class reference target not found: QueryResult [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: more than one target found for cross-reference 'UpsertItem': PowerPlatform.Dataverse.models.UpsertItem, PowerPlatform.Dataverse.models.upsert.UpsertItem [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:2308: WARNING: py:attr reference target not found: AlternateKeyInfo.status [ref.attr] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:: WARNING: py:class reference target not found: collections.abc.Iterable [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:296: WARNING: py:class reference target not found: QueryResult [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:298: WARNING: py:class reference target not found: QueryResult [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:303: WARNING: py:class reference target not found: FilterExpression [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:320: WARNING: py:class reference target not found: QueryResult [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:342: WARNING: py:class reference target not found: QueryResult [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:350: WARNING: py:class reference target not found: FilterExpression [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:367: WARNING: py:class reference target not found: QueryResult [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:: WARNING: more than one target found for cross-reference 'UpsertItem': PowerPlatform.Dataverse.models.UpsertItem, PowerPlatform.Dataverse.models.upsert.UpsertItem [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\tables\index.rst:471: WARNING: py:attr reference target not found: AlternateKeyInfo.status [ref.attr] +generating indices... genindex py-modindex done +writing additional pages... search done +dumping search index in English (code: en)... done +dumping object inventory... done +build succeeded, 119 warnings. + +The HTML pages are in docs_local\_build. diff --git a/docs_local/build_after_fixes.log b/docs_local/build_after_fixes.log new file mode 100644 index 00000000..5aff2e13 --- /dev/null +++ b/docs_local/build_after_fixes.log @@ -0,0 +1,125 @@ +Running Sphinx v9.1.0 +loading translations [en]... done +making output directory... done +[AutoAPI] Reading files... [ 2%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\__init__.py [AutoAPI] Reading files... [ 5%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\client.py [AutoAPI] Reading files... [ 7%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\_skill_installer.py [AutoAPI] Reading files... [ 9%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\__init__.py [AutoAPI] Reading files... [ 11%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\claude_skill\__init__.py [AutoAPI] Reading files... [ 14%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\constants.py [AutoAPI] Reading files... [ 16%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\__init__.py [AutoAPI] Reading files... [ 18%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\config.py [AutoAPI] Reading files... [ 20%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\errors.py [AutoAPI] Reading files... [ 23%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\log_config.py [AutoAPI] Reading files... [ 25%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_auth.py [AutoAPI] Reading files... [ 27%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_error_codes.py [AutoAPI] Reading files... [ 30%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http.py [AutoAPI] Reading files... [ 32%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http_logger.py [AutoAPI] Reading files... [ 34%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\__init__.py [AutoAPI] Reading files... [ 36%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch.py [AutoAPI] Reading files... [ 39%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata.py [AutoAPI] Reading files... [ 41%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_raw_request.py [AutoAPI] Reading files... [ 43%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_relationships.py [AutoAPI] Reading files... [ 45%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_upload.py [AutoAPI] Reading files... [ 48%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\__init__.py [AutoAPI] Reading files... [ 50%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\extensions\__init__.py [AutoAPI] Reading files... [ 52%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\migrate_v0_to_v1.py [AutoAPI] Reading files... [ 55%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\__init__.py [AutoAPI] Reading files... [ 57%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\batch.py [AutoAPI] Reading files... [ 59%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\fetchxml_query.py [AutoAPI] Reading files... [ 61%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\filters.py [AutoAPI] Reading files... [ 64%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\labels.py [AutoAPI] Reading files... [ 66%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\protocol.py [AutoAPI] Reading files... [ 68%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\query_builder.py [AutoAPI] Reading files... [ 70%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\record.py [AutoAPI] Reading files... [ 73%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\relationship.py [AutoAPI] Reading files... [ 75%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\table_info.py [AutoAPI] Reading files... [ 77%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\upsert.py [AutoAPI] Reading files... [ 80%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\__init__.py [AutoAPI] Reading files... [ 82%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\batch.py [AutoAPI] Reading files... [ 84%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\dataframe.py [AutoAPI] Reading files... [ 86%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\files.py [AutoAPI] Reading files... [ 89%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\query.py [AutoAPI] Reading files... [ 91%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\records.py [AutoAPI] Reading files... [ 93%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\tables.py [AutoAPI] Reading files... [ 95%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\__init__.py [AutoAPI] Reading files... [ 98%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\_pandas.py [AutoAPI] Reading files... [100%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\__init__.py +[AutoAPI] Mapping Data... [ 2%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\__init__.py [AutoAPI] Mapping Data... [ 5%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\client.py [AutoAPI] Mapping Data... [ 7%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\_skill_installer.py [AutoAPI] Mapping Data... [ 9%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\__init__.py [AutoAPI] Mapping Data... [ 11%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\claude_skill\__init__.py [AutoAPI] Mapping Data... [ 14%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\constants.py [AutoAPI] Mapping Data... [ 16%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\__init__.py [AutoAPI] Mapping Data... [ 18%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\config.py [AutoAPI] Mapping Data... [ 20%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\errors.py [AutoAPI] Mapping Data... [ 23%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\log_config.py [AutoAPI] Mapping Data... [ 25%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_auth.py [AutoAPI] Mapping Data... [ 27%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_error_codes.py [AutoAPI] Mapping Data... [ 30%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http.py [AutoAPI] Mapping Data... [ 32%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http_logger.py [AutoAPI] Mapping Data... [ 34%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\__init__.py [AutoAPI] Mapping Data... [ 36%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch.py [AutoAPI] Mapping Data... [ 39%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata.py [AutoAPI] Mapping Data... [ 41%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_raw_request.py [AutoAPI] Mapping Data... [ 43%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_relationships.py [AutoAPI] Mapping Data... [ 45%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_upload.py [AutoAPI] Mapping Data... [ 48%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\__init__.py [AutoAPI] Mapping Data... [ 50%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\extensions\__init__.py [AutoAPI] Mapping Data... [ 52%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\migrate_v0_to_v1.py [AutoAPI] Mapping Data... [ 55%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\__init__.py [AutoAPI] Mapping Data... [ 57%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\batch.py [AutoAPI] Mapping Data... [ 59%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\fetchxml_query.py [AutoAPI] Mapping Data... [ 61%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\filters.py [AutoAPI] Mapping Data... [ 64%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\labels.py [AutoAPI] Mapping Data... [ 66%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\protocol.py [AutoAPI] Mapping Data... [ 68%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\query_builder.py [AutoAPI] Mapping Data... [ 70%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\record.py [AutoAPI] Mapping Data... [ 73%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\relationship.py [AutoAPI] Mapping Data... [ 75%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\table_info.py [AutoAPI] Mapping Data... [ 77%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\upsert.py [AutoAPI] Mapping Data... [ 80%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\__init__.py [AutoAPI] Mapping Data... [ 82%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\batch.py [AutoAPI] Mapping Data... [ 84%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\dataframe.py [AutoAPI] Mapping Data... [ 86%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\files.py [AutoAPI] Mapping Data... [ 89%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\query.py [AutoAPI] Mapping Data... [ 91%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\records.py [AutoAPI] Mapping Data... [ 93%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\tables.py [AutoAPI] Mapping Data... [ 95%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\__init__.py [AutoAPI] Mapping Data... [ 98%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\_pandas.py [AutoAPI] Mapping Data... [100%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\__init__.py +[AutoAPI] Rendering Data... [ 3%] PowerPlatform +[AutoAPI] Rendering Data... [ 6%] PowerPlatform.Dataverse +[AutoAPI] Rendering Data... [ 9%] PowerPlatform.Dataverse.core +[AutoAPI] Rendering Data... [ 12%] PowerPlatform.Dataverse.data +[AutoAPI] Rendering Data... [ 15%] PowerPlatform.Dataverse.utils +[AutoAPI] Rendering Data... [ 18%] PowerPlatform.Dataverse.client +[AutoAPI] Rendering Data... [ 21%] PowerPlatform.Dataverse.common +[AutoAPI] Rendering Data... [ 24%] PowerPlatform.Dataverse.models +[AutoAPI] Rendering Data... [ 27%] PowerPlatform.Dataverse.migration +[AutoAPI] Rendering Data... [ 30%] PowerPlatform.Dataverse.extensions +[AutoAPI] Rendering Data... [ 33%] PowerPlatform.Dataverse.operations +[AutoAPI] Rendering Data... [ 36%] PowerPlatform.Dataverse.core.config +[AutoAPI] Rendering Data... [ 39%] PowerPlatform.Dataverse.core.errors +[AutoAPI] Rendering Data... [ 42%] PowerPlatform.Dataverse.claude_skill +[AutoAPI] Rendering Data... [ 45%] PowerPlatform.Dataverse.models.batch +[AutoAPI] Rendering Data... [ 48%] PowerPlatform.Dataverse.models.labels +[AutoAPI] Rendering Data... [ 52%] PowerPlatform.Dataverse.models.record +[AutoAPI] Rendering Data... [ 55%] PowerPlatform.Dataverse.models.upsert +[AutoAPI] Rendering Data... [ 58%] PowerPlatform.Dataverse.models.filters +[AutoAPI] Rendering Data... [ 61%] PowerPlatform.Dataverse.core.log_config +[AutoAPI] Rendering Data... [ 64%] PowerPlatform.Dataverse.models.protocol +[AutoAPI] Rendering Data... [ 67%] PowerPlatform.Dataverse.common.constants +[AutoAPI] Rendering Data... [ 70%] PowerPlatform.Dataverse.operations.batch +[AutoAPI] Rendering Data... [ 73%] PowerPlatform.Dataverse.operations.files +[AutoAPI] Rendering Data... [ 76%] PowerPlatform.Dataverse.operations.query +[AutoAPI] Rendering Data... [ 79%] PowerPlatform.Dataverse.models.table_info +[AutoAPI] Rendering Data... [ 82%] PowerPlatform.Dataverse.operations.tables +[AutoAPI] Rendering Data... [ 85%] PowerPlatform.Dataverse.operations.records +[AutoAPI] Rendering Data... [ 88%] PowerPlatform.Dataverse.models.relationship +[AutoAPI] Rendering Data... [ 91%] PowerPlatform.Dataverse.models.query_builder +[AutoAPI] Rendering Data... [ 94%] PowerPlatform.Dataverse.operations.dataframe +[AutoAPI] Rendering Data... [ 97%] PowerPlatform.Dataverse.models.fetchxml_query +[AutoAPI] Rendering Data... [100%] PowerPlatform.Dataverse.migration.migrate_v0_to_v1 + +[autosummary] generating autosummary for: index.rst +building [mo]: targets for 0 po files that are out of date +writing output...  +building [html]: targets for 1 source files that are out of date +updating environment: [new config] 35 added, 0 changed, 0 removed +reading sources... [ 3%] autoapi/PowerPlatform/Dataverse/claude_skill/index reading sources... [ 6%] autoapi/PowerPlatform/Dataverse/client/index reading sources... [ 9%] autoapi/PowerPlatform/Dataverse/common/constants/index reading sources... [ 11%] autoapi/PowerPlatform/Dataverse/common/index reading sources... [ 14%] autoapi/PowerPlatform/Dataverse/core/config/index reading sources... [ 17%] autoapi/PowerPlatform/Dataverse/core/errors/index reading sources... [ 20%] autoapi/PowerPlatform/Dataverse/core/index reading sources... [ 23%] autoapi/PowerPlatform/Dataverse/core/log_config/index reading sources... [ 26%] autoapi/PowerPlatform/Dataverse/data/index reading sources... [ 29%] autoapi/PowerPlatform/Dataverse/extensions/index reading sources... [ 31%] autoapi/PowerPlatform/Dataverse/index reading sources... [ 34%] autoapi/PowerPlatform/Dataverse/migration/index reading sources... [ 37%] autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index reading sources... [ 40%] autoapi/PowerPlatform/Dataverse/models/batch/index reading sources... [ 43%] autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index reading sources... [ 46%] autoapi/PowerPlatform/Dataverse/models/filters/index reading sources... [ 49%] autoapi/PowerPlatform/Dataverse/models/index reading sources... [ 51%] autoapi/PowerPlatform/Dataverse/models/labels/index reading sources... [ 54%] autoapi/PowerPlatform/Dataverse/models/protocol/index reading sources... [ 57%] autoapi/PowerPlatform/Dataverse/models/query_builder/index reading sources... [ 60%] autoapi/PowerPlatform/Dataverse/models/record/index reading sources... [ 63%] autoapi/PowerPlatform/Dataverse/models/relationship/index reading sources... [ 66%] autoapi/PowerPlatform/Dataverse/models/table_info/index reading sources... [ 69%] autoapi/PowerPlatform/Dataverse/models/upsert/index reading sources... [ 71%] autoapi/PowerPlatform/Dataverse/operations/batch/index reading sources... [ 74%] autoapi/PowerPlatform/Dataverse/operations/dataframe/index reading sources... [ 77%] autoapi/PowerPlatform/Dataverse/operations/files/index reading sources... [ 80%] autoapi/PowerPlatform/Dataverse/operations/index reading sources... [ 83%] autoapi/PowerPlatform/Dataverse/operations/query/index reading sources... [ 86%] autoapi/PowerPlatform/Dataverse/operations/records/index reading sources... [ 89%] autoapi/PowerPlatform/Dataverse/operations/tables/index reading sources... [ 91%] autoapi/PowerPlatform/Dataverse/utils/index reading sources... [ 94%] autoapi/PowerPlatform/index reading sources... [ 97%] autoapi/index reading sources... [100%] index +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:77: ERROR: Unexpected indentation. [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:72: WARNING: Block quote ends without a blank line; unexpected unindent. [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:78: WARNING: Definition list ends without a blank line; unexpected unindent. [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\filters\index.rst:181: ERROR: Malformed table. +Right border not aligned or missing. + ++-----------------+-----------------------------+-------------------------------------+ +| Pattern form | Example | Compiles to | ++=================+=============================+=====================================+ +| ``val%`` | ``like("Contoso%")`` | ``startswith(column,'Contoso')`` | ++-----------------+-----------------------------+-------------------------------------+ +| ``%val`` | ``like("%Ltd")`` | ``endswith(column,'Ltd')`` | ++-----------------+-----------------------------+-------------------------------------+ +| ``%val%`` | ``like("%Corp%")`` | ``contains(column,'Corp')`` | ++-----------------+-----------------------------+-------------------------------------+ +| No wildcard | ``like("Contoso")`` | ``column eq 'Contoso'`` | ++-----------------+-----------------------------+-------------------------------------+ +| Other | ``like("Con%oso")`` | :class:`ValueError` | ++-----------------+-----------------------------+-------------------------------------+ [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\query\index.rst:114: WARNING: Inline emphasis start-string without end-string. [docutils] +looking for now-outdated files... none found +pickling environment... done +checking consistency... done +preparing documents... done +copying assets...  +copying static files...  +Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\basic.css +Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\documentation_options.js +Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\language_data.js +Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\alabaster.css +copying static files: done +copying extra files...  +copying extra files: done +copying assets: done +writing output... [ 3%] autoapi/PowerPlatform/Dataverse/claude_skill/index writing output... [ 6%] autoapi/PowerPlatform/Dataverse/client/index writing output... [ 9%] autoapi/PowerPlatform/Dataverse/common/constants/index writing output... [ 11%] autoapi/PowerPlatform/Dataverse/common/index writing output... [ 14%] autoapi/PowerPlatform/Dataverse/core/config/index writing output... [ 17%] autoapi/PowerPlatform/Dataverse/core/errors/index writing output... [ 20%] autoapi/PowerPlatform/Dataverse/core/index writing output... [ 23%] autoapi/PowerPlatform/Dataverse/core/log_config/index writing output... [ 26%] autoapi/PowerPlatform/Dataverse/data/index writing output... [ 29%] autoapi/PowerPlatform/Dataverse/extensions/index writing output... [ 31%] autoapi/PowerPlatform/Dataverse/index writing output... [ 34%] autoapi/PowerPlatform/Dataverse/migration/index writing output... [ 37%] autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index writing output... [ 40%] autoapi/PowerPlatform/Dataverse/models/batch/index writing output... [ 43%] autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index writing output... [ 46%] autoapi/PowerPlatform/Dataverse/models/filters/index writing output... [ 49%] autoapi/PowerPlatform/Dataverse/models/index writing output... [ 51%] autoapi/PowerPlatform/Dataverse/models/labels/index writing output... [ 54%] autoapi/PowerPlatform/Dataverse/models/protocol/index writing output... [ 57%] autoapi/PowerPlatform/Dataverse/models/query_builder/index writing output... [ 60%] autoapi/PowerPlatform/Dataverse/models/record/index writing output... [ 63%] autoapi/PowerPlatform/Dataverse/models/relationship/index writing output... [ 66%] autoapi/PowerPlatform/Dataverse/models/table_info/index writing output... [ 69%] autoapi/PowerPlatform/Dataverse/models/upsert/index writing output... [ 71%] autoapi/PowerPlatform/Dataverse/operations/batch/index writing output... [ 74%] autoapi/PowerPlatform/Dataverse/operations/dataframe/index writing output... [ 77%] autoapi/PowerPlatform/Dataverse/operations/files/index writing output... [ 80%] autoapi/PowerPlatform/Dataverse/operations/index writing output... [ 83%] autoapi/PowerPlatform/Dataverse/operations/query/index writing output... [ 86%] autoapi/PowerPlatform/Dataverse/operations/records/index writing output... [ 89%] autoapi/PowerPlatform/Dataverse/operations/tables/index writing output... [ 91%] autoapi/PowerPlatform/Dataverse/utils/index writing output... [ 94%] autoapi/PowerPlatform/index writing output... [ 97%] autoapi/index writing output... [100%] index +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\client\index.rst:18: WARNING: py:class reference target not found: azure.core.credentials.TokenCredential [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\client\index.rst:16: WARNING: py:class reference target not found: azure.core.credentials.TokenCredential [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:33::1: WARNING: py:class reference target not found: Record [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:42::1: WARNING: py:class reference target not found: ColumnProxy [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:48: WARNING: py:class reference target not found: ColumnProxy [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:61: WARNING: py:class reference target not found: ColumnProxy [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:73: WARNING: py:class reference target not found: FilterExpression [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:154: WARNING: py:class reference target not found: Record [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:162: WARNING: py:class reference target not found: Record [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:44: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:113: WARNING: py:class reference target not found: pathlib.Path [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:487: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\record\index.rst:22: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:30: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:93: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:563: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:585: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:617: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:54: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:93: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:154: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:154: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:189: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:248: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:: WARNING: py:class reference target not found: collections.abc.Iterable [ref.class] +generating indices... genindex py-modindex done +writing additional pages... search done +dumping search index in English (code: en)... done +dumping object inventory... done +build succeeded, 43 warnings. + +The HTML pages are in docs_local\_build. diff --git a/docs_local/build_final.log b/docs_local/build_final.log new file mode 100644 index 00000000..6c418776 --- /dev/null +++ b/docs_local/build_final.log @@ -0,0 +1,130 @@ +Running Sphinx v9.1.0 +loading translations [en]... done +making output directory... done +[AutoAPI] Reading files... [ 2%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\__init__.py [AutoAPI] Reading files... [ 4%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\client.py [AutoAPI] Reading files... [ 7%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\_skill_installer.py [AutoAPI] Reading files... [ 9%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\__init__.py [AutoAPI] Reading files... [ 11%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\claude_skill\__init__.py [AutoAPI] Reading files... [ 13%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\constants.py [AutoAPI] Reading files... [ 15%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\__init__.py [AutoAPI] Reading files... [ 17%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\config.py [AutoAPI] Reading files... [ 20%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\errors.py [AutoAPI] Reading files... [ 22%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\log_config.py [AutoAPI] Reading files... [ 24%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_auth.py [AutoAPI] Reading files... [ 26%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_error_codes.py [AutoAPI] Reading files... [ 28%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http.py [AutoAPI] Reading files... [ 30%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http_logger.py [AutoAPI] Reading files... [ 33%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\__init__.py [AutoAPI] Reading files... [ 35%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch.py [AutoAPI] Reading files... [ 37%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch_base.py [AutoAPI] Reading files... [ 39%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata.py [AutoAPI] Reading files... [ 41%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata_base.py [AutoAPI] Reading files... [ 43%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_raw_request.py [AutoAPI] Reading files... [ 46%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_relationships.py [AutoAPI] Reading files... [ 48%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_upload.py [AutoAPI] Reading files... [ 50%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\__init__.py [AutoAPI] Reading files... [ 52%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\extensions\__init__.py [AutoAPI] Reading files... [ 54%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\migrate_v0_to_v1.py [AutoAPI] Reading files... [ 57%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\__init__.py [AutoAPI] Reading files... [ 59%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\batch.py [AutoAPI] Reading files... [ 61%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\fetchxml_query.py [AutoAPI] Reading files... [ 63%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\filters.py [AutoAPI] Reading files... [ 65%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\labels.py [AutoAPI] Reading files... [ 67%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\protocol.py [AutoAPI] Reading files... [ 70%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\query_builder.py [AutoAPI] Reading files... [ 72%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\record.py [AutoAPI] Reading files... [ 74%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\relationship.py [AutoAPI] Reading files... [ 76%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\table_info.py [AutoAPI] Reading files... [ 78%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\upsert.py [AutoAPI] Reading files... [ 80%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\__init__.py [AutoAPI] Reading files... [ 83%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\batch.py [AutoAPI] Reading files... [ 85%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\dataframe.py [AutoAPI] Reading files... [ 87%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\files.py [AutoAPI] Reading files... [ 89%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\query.py [AutoAPI] Reading files... [ 91%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\records.py [AutoAPI] Reading files... [ 93%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\tables.py [AutoAPI] Reading files... [ 96%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\__init__.py [AutoAPI] Reading files... [ 98%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\_pandas.py [AutoAPI] Reading files... [100%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\__init__.py +[AutoAPI] Mapping Data... [ 2%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\__init__.py [AutoAPI] Mapping Data... [ 4%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\client.py [AutoAPI] Mapping Data... [ 7%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\_skill_installer.py [AutoAPI] Mapping Data... [ 9%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\__init__.py [AutoAPI] Mapping Data... [ 11%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\claude_skill\__init__.py [AutoAPI] Mapping Data... [ 13%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\constants.py [AutoAPI] Mapping Data... [ 15%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\__init__.py [AutoAPI] Mapping Data... [ 17%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\config.py [AutoAPI] Mapping Data... [ 20%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\errors.py [AutoAPI] Mapping Data... [ 22%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\log_config.py [AutoAPI] Mapping Data... [ 24%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_auth.py [AutoAPI] Mapping Data... [ 26%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_error_codes.py [AutoAPI] Mapping Data... [ 28%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http.py [AutoAPI] Mapping Data... [ 30%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http_logger.py [AutoAPI] Mapping Data... [ 33%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\__init__.py [AutoAPI] Mapping Data... [ 35%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch.py [AutoAPI] Mapping Data... [ 37%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch_base.py [AutoAPI] Mapping Data... [ 39%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata.py [AutoAPI] Mapping Data... [ 41%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata_base.py [AutoAPI] Mapping Data... [ 43%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_raw_request.py [AutoAPI] Mapping Data... [ 46%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_relationships.py [AutoAPI] Mapping Data... [ 48%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_upload.py [AutoAPI] Mapping Data... [ 50%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\__init__.py [AutoAPI] Mapping Data... [ 52%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\extensions\__init__.py [AutoAPI] Mapping Data... [ 54%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\migrate_v0_to_v1.py [AutoAPI] Mapping Data... [ 57%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\__init__.py [AutoAPI] Mapping Data... [ 59%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\batch.py [AutoAPI] Mapping Data... [ 61%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\fetchxml_query.py [AutoAPI] Mapping Data... [ 63%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\filters.py [AutoAPI] Mapping Data... [ 65%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\labels.py [AutoAPI] Mapping Data... [ 67%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\protocol.py [AutoAPI] Mapping Data... [ 70%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\query_builder.py [AutoAPI] Mapping Data... [ 72%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\record.py [AutoAPI] Mapping Data... [ 74%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\relationship.py [AutoAPI] Mapping Data... [ 76%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\table_info.py [AutoAPI] Mapping Data... [ 78%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\upsert.py [AutoAPI] Mapping Data... [ 80%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\__init__.py [AutoAPI] Mapping Data... [ 83%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\batch.py [AutoAPI] Mapping Data... [ 85%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\dataframe.py [AutoAPI] Mapping Data... [ 87%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\files.py [AutoAPI] Mapping Data... [ 89%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\query.py [AutoAPI] Mapping Data... [ 91%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\records.py [AutoAPI] Mapping Data... [ 93%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\tables.py [AutoAPI] Mapping Data... [ 96%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\__init__.py [AutoAPI] Mapping Data... [ 98%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\_pandas.py [AutoAPI] Mapping Data... [100%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\__init__.py +[AutoAPI] Rendering Data... [ 3%] PowerPlatform +[AutoAPI] Rendering Data... [ 6%] PowerPlatform.Dataverse +[AutoAPI] Rendering Data... [ 9%] PowerPlatform.Dataverse.core +[AutoAPI] Rendering Data... [ 12%] PowerPlatform.Dataverse.data +[AutoAPI] Rendering Data... [ 15%] PowerPlatform.Dataverse.utils +[AutoAPI] Rendering Data... [ 18%] PowerPlatform.Dataverse.client +[AutoAPI] Rendering Data... [ 21%] PowerPlatform.Dataverse.common +[AutoAPI] Rendering Data... [ 24%] PowerPlatform.Dataverse.models +[AutoAPI] Rendering Data... [ 27%] PowerPlatform.Dataverse.migration +[AutoAPI] Rendering Data... [ 30%] PowerPlatform.Dataverse.extensions +[AutoAPI] Rendering Data... [ 33%] PowerPlatform.Dataverse.operations +[AutoAPI] Rendering Data... [ 36%] PowerPlatform.Dataverse.core.config +[AutoAPI] Rendering Data... [ 39%] PowerPlatform.Dataverse.core.errors +[AutoAPI] Rendering Data... [ 42%] PowerPlatform.Dataverse.claude_skill +[AutoAPI] Rendering Data... [ 45%] PowerPlatform.Dataverse.models.batch +[AutoAPI] Rendering Data... [ 48%] PowerPlatform.Dataverse.models.labels +[AutoAPI] Rendering Data... [ 52%] PowerPlatform.Dataverse.models.record +[AutoAPI] Rendering Data... [ 55%] PowerPlatform.Dataverse.models.upsert +[AutoAPI] Rendering Data... [ 58%] PowerPlatform.Dataverse.models.filters +[AutoAPI] Rendering Data... [ 61%] PowerPlatform.Dataverse.core.log_config +[AutoAPI] Rendering Data... [ 64%] PowerPlatform.Dataverse.models.protocol +[AutoAPI] Rendering Data... [ 67%] PowerPlatform.Dataverse.common.constants +[AutoAPI] Rendering Data... [ 70%] PowerPlatform.Dataverse.operations.batch +[AutoAPI] Rendering Data... [ 73%] PowerPlatform.Dataverse.operations.files +[AutoAPI] Rendering Data... [ 76%] PowerPlatform.Dataverse.operations.query +[AutoAPI] Rendering Data... [ 79%] PowerPlatform.Dataverse.models.table_info +[AutoAPI] Rendering Data... [ 82%] PowerPlatform.Dataverse.operations.tables +[AutoAPI] Rendering Data... [ 85%] PowerPlatform.Dataverse.operations.records +[AutoAPI] Rendering Data... [ 88%] PowerPlatform.Dataverse.models.relationship +[AutoAPI] Rendering Data... [ 91%] PowerPlatform.Dataverse.models.query_builder +[AutoAPI] Rendering Data... [ 94%] PowerPlatform.Dataverse.operations.dataframe +[AutoAPI] Rendering Data... [ 97%] PowerPlatform.Dataverse.models.fetchxml_query +[AutoAPI] Rendering Data... [100%] PowerPlatform.Dataverse.migration.migrate_v0_to_v1 + +[autosummary] generating autosummary for: index.rst +building [mo]: targets for 0 po files that are out of date +writing output...  +building [html]: targets for 1 source files that are out of date +updating environment: [new config] 35 added, 0 changed, 0 removed +reading sources... [ 3%] autoapi/PowerPlatform/Dataverse/claude_skill/index reading sources... [ 6%] autoapi/PowerPlatform/Dataverse/client/index reading sources... [ 9%] autoapi/PowerPlatform/Dataverse/common/constants/index reading sources... [ 11%] autoapi/PowerPlatform/Dataverse/common/index reading sources... [ 14%] autoapi/PowerPlatform/Dataverse/core/config/index reading sources... [ 17%] autoapi/PowerPlatform/Dataverse/core/errors/index reading sources... [ 20%] autoapi/PowerPlatform/Dataverse/core/index reading sources... [ 23%] autoapi/PowerPlatform/Dataverse/core/log_config/index reading sources... [ 26%] autoapi/PowerPlatform/Dataverse/data/index reading sources... [ 29%] autoapi/PowerPlatform/Dataverse/extensions/index reading sources... [ 31%] autoapi/PowerPlatform/Dataverse/index reading sources... [ 34%] autoapi/PowerPlatform/Dataverse/migration/index reading sources... [ 37%] autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index reading sources... [ 40%] autoapi/PowerPlatform/Dataverse/models/batch/index reading sources... [ 43%] autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index reading sources... [ 46%] autoapi/PowerPlatform/Dataverse/models/filters/index reading sources... [ 49%] autoapi/PowerPlatform/Dataverse/models/index reading sources... [ 51%] autoapi/PowerPlatform/Dataverse/models/labels/index reading sources... [ 54%] autoapi/PowerPlatform/Dataverse/models/protocol/index reading sources... [ 57%] autoapi/PowerPlatform/Dataverse/models/query_builder/index reading sources... [ 60%] autoapi/PowerPlatform/Dataverse/models/record/index reading sources... [ 63%] autoapi/PowerPlatform/Dataverse/models/relationship/index reading sources... [ 66%] autoapi/PowerPlatform/Dataverse/models/table_info/index reading sources... [ 69%] autoapi/PowerPlatform/Dataverse/models/upsert/index reading sources... [ 71%] autoapi/PowerPlatform/Dataverse/operations/batch/index reading sources... [ 74%] autoapi/PowerPlatform/Dataverse/operations/dataframe/index reading sources... [ 77%] autoapi/PowerPlatform/Dataverse/operations/files/index reading sources... [ 80%] autoapi/PowerPlatform/Dataverse/operations/index reading sources... [ 83%] autoapi/PowerPlatform/Dataverse/operations/query/index reading sources... [ 86%] autoapi/PowerPlatform/Dataverse/operations/records/index reading sources... [ 89%] autoapi/PowerPlatform/Dataverse/operations/tables/index reading sources... [ 91%] autoapi/PowerPlatform/Dataverse/utils/index reading sources... [ 94%] autoapi/PowerPlatform/index reading sources... [ 97%] autoapi/index reading sources... [100%] index +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:77: ERROR: Unexpected indentation. [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:72: WARNING: Block quote ends without a blank line; unexpected unindent. [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:78: WARNING: Definition list ends without a blank line; unexpected unindent. [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\filters\index.rst:181: ERROR: Malformed table. +Right border not aligned or missing. + ++-----------------+-----------------------------+-------------------------------------+ +| Pattern form | Example | Compiles to | ++=================+=============================+=====================================+ +| ``val%`` | ``like("Contoso%")`` | ``startswith(column,'Contoso')`` | ++-----------------+-----------------------------+-------------------------------------+ +| ``%val`` | ``like("%Ltd")`` | ``endswith(column,'Ltd')`` | ++-----------------+-----------------------------+-------------------------------------+ +| ``%val%`` | ``like("%Corp%")`` | ``contains(column,'Corp')`` | ++-----------------+-----------------------------+-------------------------------------+ +| No wildcard | ``like("Contoso")`` | ``column eq 'Contoso'`` | ++-----------------+-----------------------------+-------------------------------------+ +| Other | ``like("Con%oso")`` | :class:`ValueError` | ++-----------------+-----------------------------+-------------------------------------+ [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\query\index.rst:114: WARNING: Inline emphasis start-string without end-string. [docutils] +looking for now-outdated files... none found +pickling environment... done +checking consistency... done +preparing documents... done +copying assets...  +copying static files...  +Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\basic.css +Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\documentation_options.js +Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\language_data.js +Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\alabaster.css +copying static files: done +copying extra files...  +copying extra files: done +copying assets: done +writing output... [ 3%] autoapi/PowerPlatform/Dataverse/claude_skill/index writing output... [ 6%] autoapi/PowerPlatform/Dataverse/client/index writing output... [ 9%] autoapi/PowerPlatform/Dataverse/common/constants/index writing output... [ 11%] autoapi/PowerPlatform/Dataverse/common/index writing output... [ 14%] autoapi/PowerPlatform/Dataverse/core/config/index writing output... [ 17%] autoapi/PowerPlatform/Dataverse/core/errors/index writing output... [ 20%] autoapi/PowerPlatform/Dataverse/core/index writing output... [ 23%] autoapi/PowerPlatform/Dataverse/core/log_config/index writing output... [ 26%] autoapi/PowerPlatform/Dataverse/data/index writing output... [ 29%] autoapi/PowerPlatform/Dataverse/extensions/index writing output... [ 31%] autoapi/PowerPlatform/Dataverse/index writing output... [ 34%] autoapi/PowerPlatform/Dataverse/migration/index writing output... [ 37%] autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index writing output... [ 40%] autoapi/PowerPlatform/Dataverse/models/batch/index writing output... [ 43%] autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index writing output... [ 46%] autoapi/PowerPlatform/Dataverse/models/filters/index writing output... [ 49%] autoapi/PowerPlatform/Dataverse/models/index writing output... [ 51%] autoapi/PowerPlatform/Dataverse/models/labels/index writing output... [ 54%] autoapi/PowerPlatform/Dataverse/models/protocol/index writing output... [ 57%] autoapi/PowerPlatform/Dataverse/models/query_builder/index writing output... [ 60%] autoapi/PowerPlatform/Dataverse/models/record/index writing output... [ 63%] autoapi/PowerPlatform/Dataverse/models/relationship/index writing output... [ 66%] autoapi/PowerPlatform/Dataverse/models/table_info/index writing output... [ 69%] autoapi/PowerPlatform/Dataverse/models/upsert/index writing output... [ 71%] autoapi/PowerPlatform/Dataverse/operations/batch/index writing output... [ 74%] autoapi/PowerPlatform/Dataverse/operations/dataframe/index writing output... [ 77%] autoapi/PowerPlatform/Dataverse/operations/files/index writing output... [ 80%] autoapi/PowerPlatform/Dataverse/operations/index writing output... [ 83%] autoapi/PowerPlatform/Dataverse/operations/query/index writing output... [ 86%] autoapi/PowerPlatform/Dataverse/operations/records/index writing output... [ 89%] autoapi/PowerPlatform/Dataverse/operations/tables/index writing output... [ 91%] autoapi/PowerPlatform/Dataverse/utils/index writing output... [ 94%] autoapi/PowerPlatform/index writing output... [ 97%] autoapi/index writing output... [100%] index +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\client\index.rst:18: WARNING: py:class reference target not found: azure.core.credentials.TokenCredential [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\client\index.rst:16: WARNING: py:class reference target not found: azure.core.credentials.TokenCredential [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:33::1: WARNING: py:class reference target not found: Record [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:42::1: WARNING: py:class reference target not found: ColumnProxy [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:48: WARNING: py:class reference target not found: ColumnProxy [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:61: WARNING: py:class reference target not found: ColumnProxy [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:73: WARNING: py:class reference target not found: FilterExpression [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:154: WARNING: py:class reference target not found: Record [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:162: WARNING: py:class reference target not found: Record [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:44: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:113: WARNING: py:class reference target not found: pathlib.Path [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:193: WARNING: py:obj reference target not found: _QueryBuilderBase [ref.obj] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:295: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\record\index.rst:22: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:30: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:93: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:114: WARNING: py:class reference target not found: _BatchContext [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:324: WARNING: py:class reference target not found: _BatchContext [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:508: WARNING: py:class reference target not found: _BatchContext [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:539: WARNING: py:class reference target not found: _BatchContext [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:563: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:585: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:617: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:54: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:93: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:154: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:154: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:189: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:248: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:: WARNING: py:class reference target not found: collections.abc.Iterable [ref.class] +generating indices... genindex py-modindex done +writing additional pages... search done +dumping search index in English (code: en)... done +dumping object inventory... done +build succeeded, 48 warnings. + +The HTML pages are in docs_local\_build. diff --git a/docs_local/build_no_imported.log b/docs_local/build_no_imported.log new file mode 100644 index 00000000..e3b19bf7 --- /dev/null +++ b/docs_local/build_no_imported.log @@ -0,0 +1,120 @@ +Running Sphinx v9.1.0 +loading translations [en]... done +making output directory... done +[AutoAPI] Reading files... [ 2%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\__init__.py [AutoAPI] Reading files... [ 4%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\client.py [AutoAPI] Reading files... [ 7%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\_skill_installer.py [AutoAPI] Reading files... [ 9%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\__init__.py [AutoAPI] Reading files... [ 11%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\claude_skill\__init__.py [AutoAPI] Reading files... [ 13%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\constants.py [AutoAPI] Reading files... [ 15%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\__init__.py [AutoAPI] Reading files... [ 17%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\config.py [AutoAPI] Reading files... [ 20%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\errors.py [AutoAPI] Reading files... [ 22%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\log_config.py [AutoAPI] Reading files... [ 24%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_auth.py [AutoAPI] Reading files... [ 26%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_error_codes.py [AutoAPI] Reading files... [ 28%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http.py [AutoAPI] Reading files... [ 30%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http_logger.py [AutoAPI] Reading files... [ 33%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\__init__.py [AutoAPI] Reading files... [ 35%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch.py [AutoAPI] Reading files... [ 37%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch_base.py [AutoAPI] Reading files... [ 39%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata.py [AutoAPI] Reading files... [ 41%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata_base.py [AutoAPI] Reading files... [ 43%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_raw_request.py [AutoAPI] Reading files... [ 46%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_relationships.py [AutoAPI] Reading files... [ 48%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_upload.py [AutoAPI] Reading files... [ 50%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\__init__.py [AutoAPI] Reading files... [ 52%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\extensions\__init__.py [AutoAPI] Reading files... [ 54%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\migrate_v0_to_v1.py [AutoAPI] Reading files... [ 57%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\__init__.py [AutoAPI] Reading files... [ 59%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\batch.py [AutoAPI] Reading files... [ 61%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\fetchxml_query.py [AutoAPI] Reading files... [ 63%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\filters.py [AutoAPI] Reading files... [ 65%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\labels.py [AutoAPI] Reading files... [ 67%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\protocol.py [AutoAPI] Reading files... [ 70%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\query_builder.py [AutoAPI] Reading files... [ 72%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\record.py [AutoAPI] Reading files... [ 74%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\relationship.py [AutoAPI] Reading files... [ 76%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\table_info.py [AutoAPI] Reading files... [ 78%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\upsert.py [AutoAPI] Reading files... [ 80%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\__init__.py [AutoAPI] Reading files... [ 83%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\batch.py [AutoAPI] Reading files... [ 85%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\dataframe.py [AutoAPI] Reading files... [ 87%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\files.py [AutoAPI] Reading files... [ 89%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\query.py [AutoAPI] Reading files... [ 91%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\records.py [AutoAPI] Reading files... [ 93%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\tables.py [AutoAPI] Reading files... [ 96%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\__init__.py [AutoAPI] Reading files... [ 98%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\_pandas.py [AutoAPI] Reading files... [100%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\__init__.py +WARNING: Cannot resolve cyclic import: PowerPlatform.Dataverse.client, PowerPlatform.Dataverse.data._odata, PowerPlatform.Dataverse.data._odata_base, PowerPlatform.Dataverse, PowerPlatform.Dataverse.client [autoapi.python_import_resolution] +[AutoAPI] Mapping Data... [ 2%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\__init__.py [AutoAPI] Mapping Data... [ 4%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\client.py [AutoAPI] Mapping Data... [ 7%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\_skill_installer.py [AutoAPI] Mapping Data... [ 9%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\__init__.py [AutoAPI] Mapping Data... [ 11%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\claude_skill\__init__.py [AutoAPI] Mapping Data... [ 13%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\constants.py [AutoAPI] Mapping Data... [ 15%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\__init__.py [AutoAPI] Mapping Data... [ 17%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\config.py [AutoAPI] Mapping Data... [ 20%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\errors.py [AutoAPI] Mapping Data... [ 22%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\log_config.py [AutoAPI] Mapping Data... [ 24%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_auth.py [AutoAPI] Mapping Data... [ 26%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_error_codes.py [AutoAPI] Mapping Data... [ 28%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http.py [AutoAPI] Mapping Data... [ 30%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http_logger.py [AutoAPI] Mapping Data... [ 33%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\__init__.py [AutoAPI] Mapping Data... [ 35%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch.py [AutoAPI] Mapping Data... [ 37%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch_base.py [AutoAPI] Mapping Data... [ 39%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata.py [AutoAPI] Mapping Data... [ 41%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata_base.py [AutoAPI] Mapping Data... [ 43%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_raw_request.py [AutoAPI] Mapping Data... [ 46%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_relationships.py [AutoAPI] Mapping Data... [ 48%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_upload.py [AutoAPI] Mapping Data... [ 50%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\__init__.py [AutoAPI] Mapping Data... [ 52%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\extensions\__init__.py [AutoAPI] Mapping Data... [ 54%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\migrate_v0_to_v1.py [AutoAPI] Mapping Data... [ 57%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\__init__.py [AutoAPI] Mapping Data... [ 59%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\batch.py [AutoAPI] Mapping Data... [ 61%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\fetchxml_query.py [AutoAPI] Mapping Data... [ 63%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\filters.py [AutoAPI] Mapping Data... [ 65%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\labels.py [AutoAPI] Mapping Data... [ 67%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\protocol.py [AutoAPI] Mapping Data... [ 70%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\query_builder.py [AutoAPI] Mapping Data... [ 72%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\record.py [AutoAPI] Mapping Data... [ 74%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\relationship.py [AutoAPI] Mapping Data... [ 76%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\table_info.py [AutoAPI] Mapping Data... [ 78%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\upsert.py [AutoAPI] Mapping Data... [ 80%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\__init__.py [AutoAPI] Mapping Data... [ 83%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\batch.py [AutoAPI] Mapping Data... [ 85%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\dataframe.py [AutoAPI] Mapping Data... [ 87%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\files.py [AutoAPI] Mapping Data... [ 89%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\query.py [AutoAPI] Mapping Data... [ 91%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\records.py [AutoAPI] Mapping Data... [ 93%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\tables.py [AutoAPI] Mapping Data... [ 96%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\__init__.py [AutoAPI] Mapping Data... [ 98%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\_pandas.py [AutoAPI] Mapping Data... [100%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\__init__.py +[AutoAPI] Rendering Data... [ 3%] PowerPlatform +[AutoAPI] Rendering Data... [ 6%] PowerPlatform.Dataverse +[AutoAPI] Rendering Data... [ 9%] PowerPlatform.Dataverse.core +[AutoAPI] Rendering Data... [ 12%] PowerPlatform.Dataverse.data +[AutoAPI] Rendering Data... [ 15%] PowerPlatform.Dataverse.utils +[AutoAPI] Rendering Data... [ 18%] PowerPlatform.Dataverse.client +[AutoAPI] Rendering Data... [ 21%] PowerPlatform.Dataverse.common +[AutoAPI] Rendering Data... [ 24%] PowerPlatform.Dataverse.models +[AutoAPI] Rendering Data... [ 27%] PowerPlatform.Dataverse.migration +[AutoAPI] Rendering Data... [ 30%] PowerPlatform.Dataverse.extensions +[AutoAPI] Rendering Data... [ 33%] PowerPlatform.Dataverse.operations +[AutoAPI] Rendering Data... [ 36%] PowerPlatform.Dataverse.core.config +[AutoAPI] Rendering Data... [ 39%] PowerPlatform.Dataverse.core.errors +[AutoAPI] Rendering Data... [ 42%] PowerPlatform.Dataverse.claude_skill +[AutoAPI] Rendering Data... [ 45%] PowerPlatform.Dataverse.models.batch +[AutoAPI] Rendering Data... [ 48%] PowerPlatform.Dataverse.models.labels +[AutoAPI] Rendering Data... [ 52%] PowerPlatform.Dataverse.models.record +[AutoAPI] Rendering Data... [ 55%] PowerPlatform.Dataverse.models.upsert +[AutoAPI] Rendering Data... [ 58%] PowerPlatform.Dataverse.models.filters +[AutoAPI] Rendering Data... [ 61%] PowerPlatform.Dataverse.core.log_config +[AutoAPI] Rendering Data... [ 64%] PowerPlatform.Dataverse.models.protocol +[AutoAPI] Rendering Data... [ 67%] PowerPlatform.Dataverse.common.constants +[AutoAPI] Rendering Data... [ 70%] PowerPlatform.Dataverse.operations.batch +[AutoAPI] Rendering Data... [ 73%] PowerPlatform.Dataverse.operations.files +[AutoAPI] Rendering Data... [ 76%] PowerPlatform.Dataverse.operations.query +[AutoAPI] Rendering Data... [ 79%] PowerPlatform.Dataverse.models.table_info +[AutoAPI] Rendering Data... [ 82%] PowerPlatform.Dataverse.operations.tables +[AutoAPI] Rendering Data... [ 85%] PowerPlatform.Dataverse.operations.records +[AutoAPI] Rendering Data... [ 88%] PowerPlatform.Dataverse.models.relationship +[AutoAPI] Rendering Data... [ 91%] PowerPlatform.Dataverse.models.query_builder +[AutoAPI] Rendering Data... [ 94%] PowerPlatform.Dataverse.operations.dataframe +[AutoAPI] Rendering Data... [ 97%] PowerPlatform.Dataverse.models.fetchxml_query +[AutoAPI] Rendering Data... [100%] PowerPlatform.Dataverse.migration.migrate_v0_to_v1 + +[autosummary] generating autosummary for: index.rst +building [mo]: targets for 0 po files that are out of date +writing output...  +building [html]: targets for 1 source files that are out of date +updating environment: [new config] 35 added, 0 changed, 0 removed +reading sources... [ 3%] autoapi/PowerPlatform/Dataverse/claude_skill/index reading sources... [ 6%] autoapi/PowerPlatform/Dataverse/client/index reading sources... [ 9%] autoapi/PowerPlatform/Dataverse/common/constants/index reading sources... [ 11%] autoapi/PowerPlatform/Dataverse/common/index reading sources... [ 14%] autoapi/PowerPlatform/Dataverse/core/config/index reading sources... [ 17%] autoapi/PowerPlatform/Dataverse/core/errors/index reading sources... [ 20%] autoapi/PowerPlatform/Dataverse/core/index reading sources... [ 23%] autoapi/PowerPlatform/Dataverse/core/log_config/index reading sources... [ 26%] autoapi/PowerPlatform/Dataverse/data/index reading sources... [ 29%] autoapi/PowerPlatform/Dataverse/extensions/index reading sources... [ 31%] autoapi/PowerPlatform/Dataverse/index reading sources... [ 34%] autoapi/PowerPlatform/Dataverse/migration/index reading sources... [ 37%] autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index reading sources... [ 40%] autoapi/PowerPlatform/Dataverse/models/batch/index reading sources... [ 43%] autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index reading sources... [ 46%] autoapi/PowerPlatform/Dataverse/models/filters/index reading sources... [ 49%] autoapi/PowerPlatform/Dataverse/models/index reading sources... [ 51%] autoapi/PowerPlatform/Dataverse/models/labels/index reading sources... [ 54%] autoapi/PowerPlatform/Dataverse/models/protocol/index reading sources... [ 57%] autoapi/PowerPlatform/Dataverse/models/query_builder/index reading sources... [ 60%] autoapi/PowerPlatform/Dataverse/models/record/index reading sources... [ 63%] autoapi/PowerPlatform/Dataverse/models/relationship/index reading sources... [ 66%] autoapi/PowerPlatform/Dataverse/models/table_info/index reading sources... [ 69%] autoapi/PowerPlatform/Dataverse/models/upsert/index reading sources... [ 71%] autoapi/PowerPlatform/Dataverse/operations/batch/index reading sources... [ 74%] autoapi/PowerPlatform/Dataverse/operations/dataframe/index reading sources... [ 77%] autoapi/PowerPlatform/Dataverse/operations/files/index reading sources... [ 80%] autoapi/PowerPlatform/Dataverse/operations/index reading sources... [ 83%] autoapi/PowerPlatform/Dataverse/operations/query/index reading sources... [ 86%] autoapi/PowerPlatform/Dataverse/operations/records/index reading sources... [ 89%] autoapi/PowerPlatform/Dataverse/operations/tables/index reading sources... [ 91%] autoapi/PowerPlatform/Dataverse/utils/index reading sources... [ 94%] autoapi/PowerPlatform/index reading sources... [ 97%] autoapi/index reading sources... [100%] index +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:77: ERROR: Unexpected indentation. [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:72: WARNING: Block quote ends without a blank line; unexpected unindent. [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:78: WARNING: Definition list ends without a blank line; unexpected unindent. [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\filters\index.rst:181: ERROR: Malformed table. +Right border not aligned or missing. + ++-----------------+-----------------------------+-------------------------------------+ +| Pattern form | Example | Compiles to | ++=================+=============================+=====================================+ +| ``val%`` | ``like("Contoso%")`` | ``startswith(column,'Contoso')`` | ++-----------------+-----------------------------+-------------------------------------+ +| ``%val`` | ``like("%Ltd")`` | ``endswith(column,'Ltd')`` | ++-----------------+-----------------------------+-------------------------------------+ +| ``%val%`` | ``like("%Corp%")`` | ``contains(column,'Corp')`` | ++-----------------+-----------------------------+-------------------------------------+ +| No wildcard | ``like("Contoso")`` | ``column eq 'Contoso'`` | ++-----------------+-----------------------------+-------------------------------------+ +| Other | ``like("Con%oso")`` | :class:`ValueError` | ++-----------------+-----------------------------+-------------------------------------+ [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\query\index.rst:114: WARNING: Inline emphasis start-string without end-string. [docutils] +looking for now-outdated files... none found +pickling environment... done +checking consistency... done +preparing documents... done +copying assets...  +copying static files...  +Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\basic.css +Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\documentation_options.js +Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\language_data.js +Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\alabaster.css +copying static files: done +copying extra files...  +copying extra files: done +copying assets: done +writing output... [ 3%] autoapi/PowerPlatform/Dataverse/claude_skill/index writing output... [ 6%] autoapi/PowerPlatform/Dataverse/client/index writing output... [ 9%] autoapi/PowerPlatform/Dataverse/common/constants/index writing output... [ 11%] autoapi/PowerPlatform/Dataverse/common/index writing output... [ 14%] autoapi/PowerPlatform/Dataverse/core/config/index writing output... [ 17%] autoapi/PowerPlatform/Dataverse/core/errors/index writing output... [ 20%] autoapi/PowerPlatform/Dataverse/core/index writing output... [ 23%] autoapi/PowerPlatform/Dataverse/core/log_config/index writing output... [ 26%] autoapi/PowerPlatform/Dataverse/data/index writing output... [ 29%] autoapi/PowerPlatform/Dataverse/extensions/index writing output... [ 31%] autoapi/PowerPlatform/Dataverse/index writing output... [ 34%] autoapi/PowerPlatform/Dataverse/migration/index writing output... [ 37%] autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index writing output... [ 40%] autoapi/PowerPlatform/Dataverse/models/batch/index writing output... [ 43%] autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index writing output... [ 46%] autoapi/PowerPlatform/Dataverse/models/filters/index writing output... [ 49%] autoapi/PowerPlatform/Dataverse/models/index writing output... [ 51%] autoapi/PowerPlatform/Dataverse/models/labels/index writing output... [ 54%] autoapi/PowerPlatform/Dataverse/models/protocol/index writing output... [ 57%] autoapi/PowerPlatform/Dataverse/models/query_builder/index writing output... [ 60%] autoapi/PowerPlatform/Dataverse/models/record/index writing output... [ 63%] autoapi/PowerPlatform/Dataverse/models/relationship/index writing output... [ 66%] autoapi/PowerPlatform/Dataverse/models/table_info/index writing output... [ 69%] autoapi/PowerPlatform/Dataverse/models/upsert/index writing output... [ 71%] autoapi/PowerPlatform/Dataverse/operations/batch/index writing output... [ 74%] autoapi/PowerPlatform/Dataverse/operations/dataframe/index writing output... [ 77%] autoapi/PowerPlatform/Dataverse/operations/files/index writing output... [ 80%] autoapi/PowerPlatform/Dataverse/operations/index writing output... [ 83%] autoapi/PowerPlatform/Dataverse/operations/query/index writing output... [ 86%] autoapi/PowerPlatform/Dataverse/operations/records/index writing output... [ 89%] autoapi/PowerPlatform/Dataverse/operations/tables/index writing output... [ 91%] autoapi/PowerPlatform/Dataverse/utils/index writing output... [ 94%] autoapi/PowerPlatform/index writing output... [ 97%] autoapi/index writing output... [100%] index +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\client\index.rst:18: WARNING: py:class reference target not found: azure.core.credentials.TokenCredential [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\client\index.rst:16: WARNING: py:class reference target not found: azure.core.credentials.TokenCredential [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:113: WARNING: py:class reference target not found: pathlib.Path [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:193: WARNING: py:obj reference target not found: _QueryBuilderBase [ref.obj] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:295: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\record\index.rst:22: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:30: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:93: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:114: WARNING: py:class reference target not found: _BatchContext [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:324: WARNING: py:class reference target not found: _BatchContext [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:508: WARNING: py:class reference target not found: _BatchContext [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:539: WARNING: py:class reference target not found: _BatchContext [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:563: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:585: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:617: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:54: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:93: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:154: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:154: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:189: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:248: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:: WARNING: py:class reference target not found: collections.abc.Iterable [ref.class] +generating indices... genindex py-modindex done +writing additional pages... search done +dumping search index in English (code: en)... done +dumping object inventory... done +build succeeded, 38 warnings. + +The HTML pages are in docs_local\_build. diff --git a/docs_local/build_option1.log b/docs_local/build_option1.log new file mode 100644 index 00000000..fba56aac --- /dev/null +++ b/docs_local/build_option1.log @@ -0,0 +1,140 @@ +Running Sphinx v9.1.0 +loading translations [en]... done +making output directory... done +[AutoAPI] Reading files... [ 2%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\__init__.py [AutoAPI] Reading files... [ 5%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\client.py [AutoAPI] Reading files... [ 7%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\_skill_installer.py [AutoAPI] Reading files... [ 9%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\__init__.py [AutoAPI] Reading files... [ 11%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\claude_skill\__init__.py [AutoAPI] Reading files... [ 14%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\constants.py [AutoAPI] Reading files... [ 16%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\__init__.py [AutoAPI] Reading files... [ 18%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\config.py [AutoAPI] Reading files... [ 20%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\errors.py [AutoAPI] Reading files... [ 23%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\log_config.py [AutoAPI] Reading files... [ 25%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_auth.py [AutoAPI] Reading files... [ 27%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_error_codes.py [AutoAPI] Reading files... [ 30%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http.py [AutoAPI] Reading files... [ 32%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http_logger.py [AutoAPI] Reading files... [ 34%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\__init__.py [AutoAPI] Reading files... [ 36%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch.py [AutoAPI] Reading files... [ 39%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata.py [AutoAPI] Reading files... [ 41%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_raw_request.py [AutoAPI] Reading files... [ 43%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_relationships.py [AutoAPI] Reading files... [ 45%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_upload.py [AutoAPI] Reading files... [ 48%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\__init__.py [AutoAPI] Reading files... [ 50%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\extensions\__init__.py [AutoAPI] Reading files... [ 52%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\migrate_v0_to_v1.py [AutoAPI] Reading files... [ 55%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\__init__.py [AutoAPI] Reading files... [ 57%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\batch.py [AutoAPI] Reading files... [ 59%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\fetchxml_query.py [AutoAPI] Reading files... [ 61%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\filters.py [AutoAPI] Reading files... [ 64%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\labels.py [AutoAPI] Reading files... [ 66%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\protocol.py [AutoAPI] Reading files... [ 68%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\query_builder.py [AutoAPI] Reading files... [ 70%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\record.py [AutoAPI] Reading files... [ 73%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\relationship.py [AutoAPI] Reading files... [ 75%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\table_info.py [AutoAPI] Reading files... [ 77%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\upsert.py [AutoAPI] Reading files... [ 80%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\__init__.py [AutoAPI] Reading files... [ 82%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\batch.py [AutoAPI] Reading files... [ 84%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\dataframe.py [AutoAPI] Reading files... [ 86%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\files.py [AutoAPI] Reading files... [ 89%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\query.py [AutoAPI] Reading files... [ 91%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\records.py [AutoAPI] Reading files... [ 93%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\tables.py [AutoAPI] Reading files... [ 95%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\__init__.py [AutoAPI] Reading files... [ 98%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\_pandas.py [AutoAPI] Reading files... [100%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\__init__.py +[AutoAPI] Mapping Data... [ 2%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\__init__.py [AutoAPI] Mapping Data... [ 5%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\client.py [AutoAPI] Mapping Data... [ 7%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\_skill_installer.py [AutoAPI] Mapping Data... [ 9%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\__init__.py [AutoAPI] Mapping Data... [ 11%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\claude_skill\__init__.py [AutoAPI] Mapping Data... [ 14%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\constants.py [AutoAPI] Mapping Data... [ 16%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\__init__.py [AutoAPI] Mapping Data... [ 18%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\config.py [AutoAPI] Mapping Data... [ 20%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\errors.py [AutoAPI] Mapping Data... [ 23%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\log_config.py [AutoAPI] Mapping Data... [ 25%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_auth.py [AutoAPI] Mapping Data... [ 27%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_error_codes.py [AutoAPI] Mapping Data... [ 30%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http.py [AutoAPI] Mapping Data... [ 32%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http_logger.py [AutoAPI] Mapping Data... [ 34%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\__init__.py [AutoAPI] Mapping Data... [ 36%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch.py [AutoAPI] Mapping Data... [ 39%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata.py [AutoAPI] Mapping Data... [ 41%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_raw_request.py [AutoAPI] Mapping Data... [ 43%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_relationships.py [AutoAPI] Mapping Data... [ 45%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_upload.py [AutoAPI] Mapping Data... [ 48%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\__init__.py [AutoAPI] Mapping Data... [ 50%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\extensions\__init__.py [AutoAPI] Mapping Data... [ 52%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\migrate_v0_to_v1.py [AutoAPI] Mapping Data... [ 55%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\__init__.py [AutoAPI] Mapping Data... [ 57%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\batch.py [AutoAPI] Mapping Data... [ 59%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\fetchxml_query.py [AutoAPI] Mapping Data... [ 61%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\filters.py [AutoAPI] Mapping Data... [ 64%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\labels.py [AutoAPI] Mapping Data... [ 66%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\protocol.py [AutoAPI] Mapping Data... [ 68%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\query_builder.py [AutoAPI] Mapping Data... [ 70%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\record.py [AutoAPI] Mapping Data... [ 73%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\relationship.py [AutoAPI] Mapping Data... [ 75%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\table_info.py [AutoAPI] Mapping Data... [ 77%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\upsert.py [AutoAPI] Mapping Data... [ 80%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\__init__.py [AutoAPI] Mapping Data... [ 82%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\batch.py [AutoAPI] Mapping Data... [ 84%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\dataframe.py [AutoAPI] Mapping Data... [ 86%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\files.py [AutoAPI] Mapping Data... [ 89%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\query.py [AutoAPI] Mapping Data... [ 91%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\records.py [AutoAPI] Mapping Data... [ 93%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\tables.py [AutoAPI] Mapping Data... [ 95%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\__init__.py [AutoAPI] Mapping Data... [ 98%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\_pandas.py [AutoAPI] Mapping Data... [100%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\__init__.py +[AutoAPI] Rendering Data... [ 3%] PowerPlatform +[AutoAPI] Rendering Data... [ 6%] PowerPlatform.Dataverse +[AutoAPI] Rendering Data... [ 9%] PowerPlatform.Dataverse.core +[AutoAPI] Rendering Data... [ 12%] PowerPlatform.Dataverse.data +[AutoAPI] Rendering Data... [ 15%] PowerPlatform.Dataverse.utils +[AutoAPI] Rendering Data... [ 18%] PowerPlatform.Dataverse.client +[AutoAPI] Rendering Data... [ 21%] PowerPlatform.Dataverse.common +[AutoAPI] Rendering Data... [ 24%] PowerPlatform.Dataverse.models +[AutoAPI] Rendering Data... [ 27%] PowerPlatform.Dataverse.migration +[AutoAPI] Rendering Data... [ 30%] PowerPlatform.Dataverse.extensions +[AutoAPI] Rendering Data... [ 33%] PowerPlatform.Dataverse.operations +[AutoAPI] Rendering Data... [ 36%] PowerPlatform.Dataverse.core.config +[AutoAPI] Rendering Data... [ 39%] PowerPlatform.Dataverse.core.errors +[AutoAPI] Rendering Data... [ 42%] PowerPlatform.Dataverse.claude_skill +[AutoAPI] Rendering Data... [ 45%] PowerPlatform.Dataverse.models.batch +[AutoAPI] Rendering Data... [ 48%] PowerPlatform.Dataverse.models.labels +[AutoAPI] Rendering Data... [ 52%] PowerPlatform.Dataverse.models.record +[AutoAPI] Rendering Data... [ 55%] PowerPlatform.Dataverse.models.upsert +[AutoAPI] Rendering Data... [ 58%] PowerPlatform.Dataverse.models.filters +[AutoAPI] Rendering Data... [ 61%] PowerPlatform.Dataverse.core.log_config +[AutoAPI] Rendering Data... [ 64%] PowerPlatform.Dataverse.models.protocol +[AutoAPI] Rendering Data... [ 67%] PowerPlatform.Dataverse.common.constants +[AutoAPI] Rendering Data... [ 70%] PowerPlatform.Dataverse.operations.batch +[AutoAPI] Rendering Data... [ 73%] PowerPlatform.Dataverse.operations.files +[AutoAPI] Rendering Data... [ 76%] PowerPlatform.Dataverse.operations.query +[AutoAPI] Rendering Data... [ 79%] PowerPlatform.Dataverse.models.table_info +[AutoAPI] Rendering Data... [ 82%] PowerPlatform.Dataverse.operations.tables +[AutoAPI] Rendering Data... [ 85%] PowerPlatform.Dataverse.operations.records +[AutoAPI] Rendering Data... [ 88%] PowerPlatform.Dataverse.models.relationship +[AutoAPI] Rendering Data... [ 91%] PowerPlatform.Dataverse.models.query_builder +[AutoAPI] Rendering Data... [ 94%] PowerPlatform.Dataverse.operations.dataframe +[AutoAPI] Rendering Data... [ 97%] PowerPlatform.Dataverse.models.fetchxml_query +[AutoAPI] Rendering Data... [100%] PowerPlatform.Dataverse.migration.migrate_v0_to_v1 + +[autosummary] generating autosummary for: index.rst +building [mo]: targets for 0 po files that are out of date +writing output...  +building [html]: targets for 1 source files that are out of date +updating environment: [new config] 35 added, 0 changed, 0 removed +reading sources... [ 3%] autoapi/PowerPlatform/Dataverse/claude_skill/index reading sources... [ 6%] autoapi/PowerPlatform/Dataverse/client/index reading sources... [ 9%] autoapi/PowerPlatform/Dataverse/common/constants/index reading sources... [ 11%] autoapi/PowerPlatform/Dataverse/common/index reading sources... [ 14%] autoapi/PowerPlatform/Dataverse/core/config/index reading sources... [ 17%] autoapi/PowerPlatform/Dataverse/core/errors/index reading sources... [ 20%] autoapi/PowerPlatform/Dataverse/core/index reading sources... [ 23%] autoapi/PowerPlatform/Dataverse/core/log_config/index reading sources... [ 26%] autoapi/PowerPlatform/Dataverse/data/index reading sources... [ 29%] autoapi/PowerPlatform/Dataverse/extensions/index reading sources... [ 31%] autoapi/PowerPlatform/Dataverse/index reading sources... [ 34%] autoapi/PowerPlatform/Dataverse/migration/index reading sources... [ 37%] autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index reading sources... [ 40%] autoapi/PowerPlatform/Dataverse/models/batch/index reading sources... [ 43%] autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index reading sources... [ 46%] autoapi/PowerPlatform/Dataverse/models/filters/index reading sources... [ 49%] autoapi/PowerPlatform/Dataverse/models/index reading sources... [ 51%] autoapi/PowerPlatform/Dataverse/models/labels/index reading sources... [ 54%] autoapi/PowerPlatform/Dataverse/models/protocol/index reading sources... [ 57%] autoapi/PowerPlatform/Dataverse/models/query_builder/index reading sources... [ 60%] autoapi/PowerPlatform/Dataverse/models/record/index reading sources... [ 63%] autoapi/PowerPlatform/Dataverse/models/relationship/index reading sources... [ 66%] autoapi/PowerPlatform/Dataverse/models/table_info/index reading sources... [ 69%] autoapi/PowerPlatform/Dataverse/models/upsert/index reading sources... [ 71%] autoapi/PowerPlatform/Dataverse/operations/batch/index reading sources... [ 74%] autoapi/PowerPlatform/Dataverse/operations/dataframe/index reading sources... [ 77%] autoapi/PowerPlatform/Dataverse/operations/files/index reading sources... [ 80%] autoapi/PowerPlatform/Dataverse/operations/index reading sources... [ 83%] autoapi/PowerPlatform/Dataverse/operations/query/index reading sources... [ 86%] autoapi/PowerPlatform/Dataverse/operations/records/index reading sources... [ 89%] autoapi/PowerPlatform/Dataverse/operations/tables/index reading sources... [ 91%] autoapi/PowerPlatform/Dataverse/utils/index reading sources... [ 94%] autoapi/PowerPlatform/index reading sources... [ 97%] autoapi/index reading sources... [100%] index +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:77: ERROR: Unexpected indentation. [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:72: WARNING: Block quote ends without a blank line; unexpected unindent. [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:78: WARNING: Definition list ends without a blank line; unexpected unindent. [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\filters\index.rst:181: ERROR: Malformed table. +Right border not aligned or missing. + ++-----------------+-----------------------------+-------------------------------------+ +| Pattern form | Example | Compiles to | ++=================+=============================+=====================================+ +| ``val%`` | ``like("Contoso%")`` | ``startswith(column,'Contoso')`` | ++-----------------+-----------------------------+-------------------------------------+ +| ``%val`` | ``like("%Ltd")`` | ``endswith(column,'Ltd')`` | ++-----------------+-----------------------------+-------------------------------------+ +| ``%val%`` | ``like("%Corp%")`` | ``contains(column,'Corp')`` | ++-----------------+-----------------------------+-------------------------------------+ +| No wildcard | ``like("Contoso")`` | ``column eq 'Contoso'`` | ++-----------------+-----------------------------+-------------------------------------+ +| Other | ``like("Con%oso")`` | :class:`ValueError` | ++-----------------+-----------------------------+-------------------------------------+ [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\query\index.rst:114: WARNING: Inline emphasis start-string without end-string. [docutils] +looking for now-outdated files... none found +pickling environment... done +checking consistency... done +preparing documents... done +copying assets...  +copying static files...  +Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\basic.css +Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\documentation_options.js +Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\language_data.js +Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\alabaster.css +copying static files: done +copying extra files...  +copying extra files: done +copying assets: done +writing output... [ 3%] autoapi/PowerPlatform/Dataverse/claude_skill/index writing output... [ 6%] autoapi/PowerPlatform/Dataverse/client/index writing output... [ 9%] autoapi/PowerPlatform/Dataverse/common/constants/index writing output... [ 11%] autoapi/PowerPlatform/Dataverse/common/index writing output... [ 14%] autoapi/PowerPlatform/Dataverse/core/config/index writing output... [ 17%] autoapi/PowerPlatform/Dataverse/core/errors/index writing output... [ 20%] autoapi/PowerPlatform/Dataverse/core/index writing output... [ 23%] autoapi/PowerPlatform/Dataverse/core/log_config/index writing output... [ 26%] autoapi/PowerPlatform/Dataverse/data/index writing output... [ 29%] autoapi/PowerPlatform/Dataverse/extensions/index writing output... [ 31%] autoapi/PowerPlatform/Dataverse/index writing output... [ 34%] autoapi/PowerPlatform/Dataverse/migration/index writing output... [ 37%] autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index writing output... [ 40%] autoapi/PowerPlatform/Dataverse/models/batch/index writing output... [ 43%] autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index writing output... [ 46%] autoapi/PowerPlatform/Dataverse/models/filters/index writing output... [ 49%] autoapi/PowerPlatform/Dataverse/models/index writing output... [ 51%] autoapi/PowerPlatform/Dataverse/models/labels/index writing output... [ 54%] autoapi/PowerPlatform/Dataverse/models/protocol/index writing output... [ 57%] autoapi/PowerPlatform/Dataverse/models/query_builder/index writing output... [ 60%] autoapi/PowerPlatform/Dataverse/models/record/index writing output... [ 63%] autoapi/PowerPlatform/Dataverse/models/relationship/index writing output... [ 66%] autoapi/PowerPlatform/Dataverse/models/table_info/index writing output... [ 69%] autoapi/PowerPlatform/Dataverse/models/upsert/index writing output... [ 71%] autoapi/PowerPlatform/Dataverse/operations/batch/index writing output... [ 74%] autoapi/PowerPlatform/Dataverse/operations/dataframe/index writing output... [ 77%] autoapi/PowerPlatform/Dataverse/operations/files/index writing output... [ 80%] autoapi/PowerPlatform/Dataverse/operations/index writing output... [ 83%] autoapi/PowerPlatform/Dataverse/operations/query/index writing output... [ 86%] autoapi/PowerPlatform/Dataverse/operations/records/index writing output... [ 89%] autoapi/PowerPlatform/Dataverse/operations/tables/index writing output... [ 91%] autoapi/PowerPlatform/Dataverse/utils/index writing output... [ 94%] autoapi/PowerPlatform/index writing output... [ 97%] autoapi/index writing output... [100%] index +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\client\index.rst:18: WARNING: py:class reference target not found: azure.core.credentials.TokenCredential [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\client\index.rst:22: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._odata._ODataClient [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\client\index.rst:16: WARNING: py:class reference target not found: azure.core.credentials.TokenCredential [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:33::1: WARNING: py:class reference target not found: Record [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:42::1: WARNING: py:class reference target not found: ColumnProxy [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:48: WARNING: py:class reference target not found: ColumnProxy [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:61: WARNING: py:class reference target not found: ColumnProxy [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:73: WARNING: py:class reference target not found: FilterExpression [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:154: WARNING: py:class reference target not found: Record [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:162: WARNING: py:class reference target not found: Record [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:44: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:113: WARNING: py:class reference target not found: pathlib.Path [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\fetchxml_query\index.rst:37: WARNING: py:class reference target not found: QueryResult [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\fetchxml_query\index.rst:55: WARNING: py:class reference target not found: QueryResult [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\fetchxml_query\index.rst:67: WARNING: py:class reference target not found: QueryResult [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:487: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\record\index.rst:22: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:30: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:93: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:290: WARNING: py:class reference target not found: FilterExpression [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:413: WARNING: py:attr reference target not found: BatchResult.responses [ref.attr] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:427: WARNING: py:attr reference target not found: BatchResult.responses [ref.attr] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:563: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:585: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:617: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:54: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:93: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:154: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:154: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:189: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:248: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:: WARNING: py:class reference target not found: collections.abc.Iterable [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:296: WARNING: py:class reference target not found: QueryResult [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:298: WARNING: py:class reference target not found: QueryResult [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:303: WARNING: py:class reference target not found: FilterExpression [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:320: WARNING: py:class reference target not found: QueryResult [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:342: WARNING: py:class reference target not found: QueryResult [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:350: WARNING: py:class reference target not found: FilterExpression [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:367: WARNING: py:class reference target not found: QueryResult [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\tables\index.rst:471: WARNING: py:attr reference target not found: AlternateKeyInfo.status [ref.attr] +generating indices... genindex py-modindex done +writing additional pages... search done +dumping search index in English (code: en)... done +dumping object inventory... done +build succeeded, 58 warnings. + +The HTML pages are in docs_local\_build. diff --git a/docs_local/build_optionA.log b/docs_local/build_optionA.log new file mode 100644 index 00000000..b8e0129a --- /dev/null +++ b/docs_local/build_optionA.log @@ -0,0 +1,200 @@ +Running Sphinx v9.1.0 +loading translations [en]... done +making output directory... done +[AutoAPI] Reading files... [ 2%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\__init__.py [AutoAPI] Reading files... [ 4%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\client.py [AutoAPI] Reading files... [ 7%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\_skill_installer.py [AutoAPI] Reading files... [ 9%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\__init__.py [AutoAPI] Reading files... [ 11%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\claude_skill\__init__.py [AutoAPI] Reading files... [ 13%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\constants.py [AutoAPI] Reading files... [ 15%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\__init__.py [AutoAPI] Reading files... [ 17%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\config.py [AutoAPI] Reading files... [ 20%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\errors.py [AutoAPI] Reading files... [ 22%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\log_config.py [AutoAPI] Reading files... [ 24%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_auth.py [AutoAPI] Reading files... [ 26%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_error_codes.py [AutoAPI] Reading files... [ 28%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http.py [AutoAPI] Reading files... [ 30%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http_logger.py [AutoAPI] Reading files... [ 33%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\__init__.py [AutoAPI] Reading files... [ 35%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch.py [AutoAPI] Reading files... [ 37%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch_base.py [AutoAPI] Reading files... [ 39%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata.py [AutoAPI] Reading files... [ 41%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata_base.py [AutoAPI] Reading files... [ 43%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_raw_request.py [AutoAPI] Reading files... [ 46%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_relationships.py [AutoAPI] Reading files... [ 48%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_upload.py [AutoAPI] Reading files... [ 50%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\__init__.py [AutoAPI] Reading files... [ 52%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\extensions\__init__.py [AutoAPI] Reading files... [ 54%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\migrate_v0_to_v1.py [AutoAPI] Reading files... [ 57%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\__init__.py [AutoAPI] Reading files... [ 59%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\batch.py [AutoAPI] Reading files... [ 61%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\fetchxml_query.py [AutoAPI] Reading files... [ 63%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\filters.py [AutoAPI] Reading files... [ 65%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\labels.py [AutoAPI] Reading files... [ 67%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\protocol.py [AutoAPI] Reading files... [ 70%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\query_builder.py [AutoAPI] Reading files... [ 72%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\record.py [AutoAPI] Reading files... [ 74%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\relationship.py [AutoAPI] Reading files... [ 76%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\table_info.py [AutoAPI] Reading files... [ 78%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\upsert.py [AutoAPI] Reading files... [ 80%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\__init__.py [AutoAPI] Reading files... [ 83%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\batch.py [AutoAPI] Reading files... [ 85%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\dataframe.py [AutoAPI] Reading files... [ 87%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\files.py [AutoAPI] Reading files... [ 89%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\query.py [AutoAPI] Reading files... [ 91%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\records.py [AutoAPI] Reading files... [ 93%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\tables.py [AutoAPI] Reading files... [ 96%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\__init__.py [AutoAPI] Reading files... [ 98%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\_pandas.py [AutoAPI] Reading files... [100%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\__init__.py +WARNING: Cannot resolve cyclic import: PowerPlatform.Dataverse.client, PowerPlatform.Dataverse.data._odata, PowerPlatform.Dataverse.data._odata_base, PowerPlatform.Dataverse, PowerPlatform.Dataverse.client [autoapi.python_import_resolution] +[AutoAPI] Mapping Data... [ 2%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\__init__.py [AutoAPI] Mapping Data... [ 4%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\client.py [AutoAPI] Mapping Data... [ 7%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\_skill_installer.py [AutoAPI] Mapping Data... [ 9%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\__init__.py [AutoAPI] Mapping Data... [ 11%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\claude_skill\__init__.py [AutoAPI] Mapping Data... [ 13%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\constants.py [AutoAPI] Mapping Data... [ 15%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\__init__.py [AutoAPI] Mapping Data... [ 17%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\config.py [AutoAPI] Mapping Data... [ 20%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\errors.py [AutoAPI] Mapping Data... [ 22%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\log_config.py [AutoAPI] Mapping Data... [ 24%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_auth.py [AutoAPI] Mapping Data... [ 26%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_error_codes.py [AutoAPI] Mapping Data... [ 28%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http.py [AutoAPI] Mapping Data... [ 30%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http_logger.py [AutoAPI] Mapping Data... [ 33%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\__init__.py [AutoAPI] Mapping Data... [ 35%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch.py [AutoAPI] Mapping Data... [ 37%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch_base.py [AutoAPI] Mapping Data... [ 39%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata.py [AutoAPI] Mapping Data... [ 41%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata_base.py [AutoAPI] Mapping Data... [ 43%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_raw_request.py [AutoAPI] Mapping Data... [ 46%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_relationships.py [AutoAPI] Mapping Data... [ 48%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_upload.py [AutoAPI] Mapping Data... [ 50%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\__init__.py [AutoAPI] Mapping Data... [ 52%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\extensions\__init__.py [AutoAPI] Mapping Data... [ 54%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\migrate_v0_to_v1.py [AutoAPI] Mapping Data... [ 57%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\__init__.py [AutoAPI] Mapping Data... [ 59%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\batch.py [AutoAPI] Mapping Data... [ 61%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\fetchxml_query.py [AutoAPI] Mapping Data... [ 63%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\filters.py [AutoAPI] Mapping Data... [ 65%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\labels.py [AutoAPI] Mapping Data... [ 67%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\protocol.py [AutoAPI] Mapping Data... [ 70%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\query_builder.py [AutoAPI] Mapping Data... [ 72%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\record.py [AutoAPI] Mapping Data... [ 74%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\relationship.py [AutoAPI] Mapping Data... [ 76%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\table_info.py [AutoAPI] Mapping Data... [ 78%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\upsert.py [AutoAPI] Mapping Data... [ 80%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\__init__.py [AutoAPI] Mapping Data... [ 83%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\batch.py [AutoAPI] Mapping Data... [ 85%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\dataframe.py [AutoAPI] Mapping Data... [ 87%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\files.py [AutoAPI] Mapping Data... [ 89%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\query.py [AutoAPI] Mapping Data... [ 91%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\records.py [AutoAPI] Mapping Data... [ 93%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\tables.py [AutoAPI] Mapping Data... [ 96%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\__init__.py [AutoAPI] Mapping Data... [ 98%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\_pandas.py [AutoAPI] Mapping Data... [100%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\__init__.py +[AutoAPI] Rendering Data... [ 3%] PowerPlatform +[AutoAPI] Rendering Data... [ 6%] PowerPlatform.Dataverse +[AutoAPI] Rendering Data... [ 9%] PowerPlatform.Dataverse.core +[AutoAPI] Rendering Data... [ 12%] PowerPlatform.Dataverse.data +[AutoAPI] Rendering Data... [ 15%] PowerPlatform.Dataverse.utils +[AutoAPI] Rendering Data... [ 18%] PowerPlatform.Dataverse.client +[AutoAPI] Rendering Data... [ 21%] PowerPlatform.Dataverse.common +[AutoAPI] Rendering Data... [ 24%] PowerPlatform.Dataverse.models +[AutoAPI] Rendering Data... [ 27%] PowerPlatform.Dataverse.migration +[AutoAPI] Rendering Data... [ 30%] PowerPlatform.Dataverse.extensions +[AutoAPI] Rendering Data... [ 33%] PowerPlatform.Dataverse.operations +[AutoAPI] Rendering Data... [ 36%] PowerPlatform.Dataverse.core.config +[AutoAPI] Rendering Data... [ 39%] PowerPlatform.Dataverse.core.errors +[AutoAPI] Rendering Data... [ 42%] PowerPlatform.Dataverse.claude_skill +[AutoAPI] Rendering Data... [ 45%] PowerPlatform.Dataverse.models.batch +[AutoAPI] Rendering Data... [ 48%] PowerPlatform.Dataverse.models.labels +[AutoAPI] Rendering Data... [ 52%] PowerPlatform.Dataverse.models.record +[AutoAPI] Rendering Data... [ 55%] PowerPlatform.Dataverse.models.upsert +[AutoAPI] Rendering Data... [ 58%] PowerPlatform.Dataverse.models.filters +[AutoAPI] Rendering Data... [ 61%] PowerPlatform.Dataverse.core.log_config +[AutoAPI] Rendering Data... [ 64%] PowerPlatform.Dataverse.models.protocol +[AutoAPI] Rendering Data... [ 67%] PowerPlatform.Dataverse.common.constants +[AutoAPI] Rendering Data... [ 70%] PowerPlatform.Dataverse.operations.batch +[AutoAPI] Rendering Data... [ 73%] PowerPlatform.Dataverse.operations.files +[AutoAPI] Rendering Data... [ 76%] PowerPlatform.Dataverse.operations.query +[AutoAPI] Rendering Data... [ 79%] PowerPlatform.Dataverse.models.table_info +[AutoAPI] Rendering Data... [ 82%] PowerPlatform.Dataverse.operations.tables +[AutoAPI] Rendering Data... [ 85%] PowerPlatform.Dataverse.operations.records +[AutoAPI] Rendering Data... [ 88%] PowerPlatform.Dataverse.models.relationship +[AutoAPI] Rendering Data... [ 91%] PowerPlatform.Dataverse.models.query_builder +[AutoAPI] Rendering Data... [ 94%] PowerPlatform.Dataverse.operations.dataframe +[AutoAPI] Rendering Data... [ 97%] PowerPlatform.Dataverse.models.fetchxml_query +[AutoAPI] Rendering Data... [100%] PowerPlatform.Dataverse.migration.migrate_v0_to_v1 + +[autosummary] generating autosummary for: index.rst +building [mo]: targets for 0 po files that are out of date +writing output...  +building [html]: targets for 1 source files that are out of date +updating environment: [new config] 35 added, 0 changed, 0 removed +reading sources... [ 3%] autoapi/PowerPlatform/Dataverse/claude_skill/index reading sources... [ 6%] autoapi/PowerPlatform/Dataverse/client/index reading sources... [ 9%] autoapi/PowerPlatform/Dataverse/common/constants/index reading sources... [ 11%] autoapi/PowerPlatform/Dataverse/common/index reading sources... [ 14%] autoapi/PowerPlatform/Dataverse/core/config/index reading sources... [ 17%] autoapi/PowerPlatform/Dataverse/core/errors/index reading sources... [ 20%] autoapi/PowerPlatform/Dataverse/core/index reading sources... [ 23%] autoapi/PowerPlatform/Dataverse/core/log_config/index reading sources... [ 26%] autoapi/PowerPlatform/Dataverse/data/index reading sources... [ 29%] autoapi/PowerPlatform/Dataverse/extensions/index reading sources... [ 31%] autoapi/PowerPlatform/Dataverse/index reading sources... [ 34%] autoapi/PowerPlatform/Dataverse/migration/index reading sources... [ 37%] autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index reading sources... [ 40%] autoapi/PowerPlatform/Dataverse/models/batch/index reading sources... [ 43%] autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index reading sources... [ 46%] autoapi/PowerPlatform/Dataverse/models/filters/index reading sources... [ 49%] autoapi/PowerPlatform/Dataverse/models/index reading sources... [ 51%] autoapi/PowerPlatform/Dataverse/models/labels/index reading sources... [ 54%] autoapi/PowerPlatform/Dataverse/models/protocol/index reading sources... [ 57%] autoapi/PowerPlatform/Dataverse/models/query_builder/index reading sources... [ 60%] autoapi/PowerPlatform/Dataverse/models/record/index reading sources... [ 63%] autoapi/PowerPlatform/Dataverse/models/relationship/index reading sources... [ 66%] autoapi/PowerPlatform/Dataverse/models/table_info/index reading sources... [ 69%] autoapi/PowerPlatform/Dataverse/models/upsert/index reading sources... [ 71%] autoapi/PowerPlatform/Dataverse/operations/batch/index reading sources... [ 74%] autoapi/PowerPlatform/Dataverse/operations/dataframe/index reading sources... [ 77%] autoapi/PowerPlatform/Dataverse/operations/files/index reading sources... [ 80%] autoapi/PowerPlatform/Dataverse/operations/index reading sources... [ 83%] autoapi/PowerPlatform/Dataverse/operations/query/index reading sources... [ 86%] autoapi/PowerPlatform/Dataverse/operations/records/index reading sources... [ 89%] autoapi/PowerPlatform/Dataverse/operations/tables/index reading sources... [ 91%] autoapi/PowerPlatform/Dataverse/utils/index reading sources... [ 94%] autoapi/PowerPlatform/index reading sources... [ 97%] autoapi/index reading sources... [100%] index +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:77: ERROR: Unexpected indentation. [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:72: WARNING: Block quote ends without a blank line; unexpected unindent. [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:78: WARNING: Definition list ends without a blank line; unexpected unindent. [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\filters\index.rst:181: ERROR: Malformed table. +Right border not aligned or missing. + ++-----------------+-----------------------------+-------------------------------------+ +| Pattern form | Example | Compiles to | ++=================+=============================+=====================================+ +| ``val%`` | ``like("Contoso%")`` | ``startswith(column,'Contoso')`` | ++-----------------+-----------------------------+-------------------------------------+ +| ``%val`` | ``like("%Ltd")`` | ``endswith(column,'Ltd')`` | ++-----------------+-----------------------------+-------------------------------------+ +| ``%val%`` | ``like("%Corp%")`` | ``contains(column,'Corp')`` | ++-----------------+-----------------------------+-------------------------------------+ +| No wildcard | ``like("Contoso")`` | ``column eq 'Contoso'`` | ++-----------------+-----------------------------+-------------------------------------+ +| Other | ``like("Con%oso")`` | :class:`ValueError` | ++-----------------+-----------------------------+-------------------------------------+ [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\index.rst:354: ERROR: Malformed table. +Right border not aligned or missing. + ++-----------------+-----------------------------+-------------------------------------+ +| Pattern form | Example | Compiles to | ++=================+=============================+=====================================+ +| ``val%`` | ``like("Contoso%")`` | ``startswith(column,'Contoso')`` | ++-----------------+-----------------------------+-------------------------------------+ +| ``%val`` | ``like("%Ltd")`` | ``endswith(column,'Ltd')`` | ++-----------------+-----------------------------+-------------------------------------+ +| ``%val%`` | ``like("%Corp%")`` | ``contains(column,'Corp')`` | ++-----------------+-----------------------------+-------------------------------------+ +| No wildcard | ``like("Contoso")`` | ``column eq 'Contoso'`` | ++-----------------+-----------------------------+-------------------------------------+ +| Other | ``like("Con%oso")`` | :class:`ValueError` | ++-----------------+-----------------------------+-------------------------------------+ [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:1189: WARNING: Inline emphasis start-string without end-string. [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\query\index.rst:114: WARNING: Inline emphasis start-string without end-string. [docutils] +looking for now-outdated files... none found +pickling environment... done +checking consistency... done +preparing documents... done +copying assets...  +copying static files...  +Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\basic.css +Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\documentation_options.js +Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\language_data.js +Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\alabaster.css +copying static files: done +copying extra files...  +copying extra files: done +copying assets: done +writing output... [ 3%] autoapi/PowerPlatform/Dataverse/claude_skill/index writing output... [ 6%] autoapi/PowerPlatform/Dataverse/client/index writing output... [ 9%] autoapi/PowerPlatform/Dataverse/common/constants/index writing output... [ 11%] autoapi/PowerPlatform/Dataverse/common/index writing output... [ 14%] autoapi/PowerPlatform/Dataverse/core/config/index writing output... [ 17%] autoapi/PowerPlatform/Dataverse/core/errors/index writing output... [ 20%] autoapi/PowerPlatform/Dataverse/core/index writing output... [ 23%] autoapi/PowerPlatform/Dataverse/core/log_config/index writing output... [ 26%] autoapi/PowerPlatform/Dataverse/data/index writing output... [ 29%] autoapi/PowerPlatform/Dataverse/extensions/index writing output... [ 31%] autoapi/PowerPlatform/Dataverse/index writing output... [ 34%] autoapi/PowerPlatform/Dataverse/migration/index writing output... [ 37%] autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index writing output... [ 40%] autoapi/PowerPlatform/Dataverse/models/batch/index writing output... [ 43%] autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index writing output... [ 46%] autoapi/PowerPlatform/Dataverse/models/filters/index writing output... [ 49%] autoapi/PowerPlatform/Dataverse/models/index writing output... [ 51%] autoapi/PowerPlatform/Dataverse/models/labels/index writing output... [ 54%] autoapi/PowerPlatform/Dataverse/models/protocol/index writing output... [ 57%] autoapi/PowerPlatform/Dataverse/models/query_builder/index writing output... [ 60%] autoapi/PowerPlatform/Dataverse/models/record/index writing output... [ 63%] autoapi/PowerPlatform/Dataverse/models/relationship/index writing output... [ 66%] autoapi/PowerPlatform/Dataverse/models/table_info/index writing output... [ 69%] autoapi/PowerPlatform/Dataverse/models/upsert/index writing output... [ 71%] autoapi/PowerPlatform/Dataverse/operations/batch/index writing output... [ 74%] autoapi/PowerPlatform/Dataverse/operations/dataframe/index writing output... [ 77%] autoapi/PowerPlatform/Dataverse/operations/files/index writing output... [ 80%] autoapi/PowerPlatform/Dataverse/operations/index writing output... [ 83%] autoapi/PowerPlatform/Dataverse/operations/query/index writing output... [ 86%] autoapi/PowerPlatform/Dataverse/operations/records/index writing output... [ 89%] autoapi/PowerPlatform/Dataverse/operations/tables/index writing output... [ 91%] autoapi/PowerPlatform/Dataverse/utils/index writing output... [ 94%] autoapi/PowerPlatform/index writing output... [ 97%] autoapi/index writing output... [100%] index +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\client\index.rst:18: WARNING: py:class reference target not found: azure.core.credentials.TokenCredential [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\client\index.rst:16: WARNING: py:class reference target not found: azure.core.credentials.TokenCredential [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:33::1: WARNING: py:class reference target not found: Record [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:42::1: WARNING: py:class reference target not found: ColumnProxy [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:46: WARNING: more than one target found for cross-reference 'ColumnProxy': PowerPlatform.Dataverse.models.filters.ColumnProxy, PowerPlatform.Dataverse.models.ColumnProxy [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:48: WARNING: py:class reference target not found: ColumnProxy [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:61: WARNING: py:class reference target not found: ColumnProxy [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:65: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:73: WARNING: py:class reference target not found: FilterExpression [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:152: WARNING: more than one target found for cross-reference 'Record': PowerPlatform.Dataverse.models.Record, PowerPlatform.Dataverse.models.record.Record [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:154: WARNING: py:class reference target not found: Record [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:162: WARNING: py:class reference target not found: Record [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:165: WARNING: more than one target found for cross-reference 'Record': PowerPlatform.Dataverse.models.Record, PowerPlatform.Dataverse.models.record.Record [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:169: WARNING: more than one target found for cross-reference 'Record': PowerPlatform.Dataverse.models.Record, PowerPlatform.Dataverse.models.record.Record [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:44: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:113: WARNING: py:class reference target not found: pathlib.Path [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\index.rst:671: WARNING: py:obj reference target not found: _QueryBuilderBase [ref.obj] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\index.rst:773: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\index.rst:81: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:193: WARNING: py:obj reference target not found: _QueryBuilderBase [ref.obj] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.QueryResult, PowerPlatform.Dataverse.models.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.QueryResult, PowerPlatform.Dataverse.models.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.QueryResult, PowerPlatform.Dataverse.models.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:295: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\record\index.rst:22: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\relationship\index.rst:25: WARNING: more than one target found for cross-reference 'Label': PowerPlatform.Dataverse.models.Label, PowerPlatform.Dataverse.models.labels.Label [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\relationship\index.rst:25: WARNING: more than one target found for cross-reference 'Label': PowerPlatform.Dataverse.models.Label, PowerPlatform.Dataverse.models.labels.Label [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:30: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:93: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:114: WARNING: py:class reference target not found: _BatchContext [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:275: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:324: WARNING: py:class reference target not found: _BatchContext [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:508: WARNING: py:class reference target not found: _BatchContext [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:539: WARNING: py:class reference target not found: _BatchContext [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:563: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:585: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:617: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:28: WARNING: more than one target found for cross-reference 'ValidationError': PowerPlatform.Dataverse.core.errors.ValidationError, PowerPlatform.Dataverse.core.ValidationError [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:28: WARNING: more than one target found for cross-reference 'MetadataError': PowerPlatform.Dataverse.core.errors.MetadataError, PowerPlatform.Dataverse.core.MetadataError [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:28: WARNING: more than one target found for cross-reference 'HttpError': PowerPlatform.Dataverse.core.errors.HttpError, PowerPlatform.Dataverse.core.HttpError [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:54: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:93: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:154: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:154: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:189: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:248: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:52: WARNING: py:class reference target not found: _BatchContext [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:76: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:98: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:130: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:176: WARNING: py:class reference target not found: _BatchContext [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:207: WARNING: py:class reference target not found: _BatchContext [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:368: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:50: WARNING: more than one target found for cross-reference 'ValidationError': PowerPlatform.Dataverse.core.errors.ValidationError, PowerPlatform.Dataverse.core.ValidationError [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:50: WARNING: more than one target found for cross-reference 'MetadataError': PowerPlatform.Dataverse.core.errors.MetadataError, PowerPlatform.Dataverse.core.MetadataError [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:50: WARNING: more than one target found for cross-reference 'HttpError': PowerPlatform.Dataverse.core.errors.HttpError, PowerPlatform.Dataverse.core.HttpError [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:496: WARNING: py:class reference target not found: _BatchContext [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:680: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:701: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:795: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:834: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:895: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:895: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:930: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:989: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: collections.abc.Iterable [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: more than one target found for cross-reference 'UpsertItem': PowerPlatform.Dataverse.models.UpsertItem, PowerPlatform.Dataverse.models.upsert.UpsertItem [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:: WARNING: py:class reference target not found: collections.abc.Iterable [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:: WARNING: more than one target found for cross-reference 'UpsertItem': PowerPlatform.Dataverse.models.UpsertItem, PowerPlatform.Dataverse.models.upsert.UpsertItem [ref.python] +generating indices... genindex py-modindex done +writing additional pages... search done +dumping search index in English (code: en)... done +dumping object inventory... done +build succeeded, 103 warnings. + +The HTML pages are in docs_local\_build. diff --git a/docs_local/build_post_merge.log b/docs_local/build_post_merge.log new file mode 100644 index 00000000..158a0555 --- /dev/null +++ b/docs_local/build_post_merge.log @@ -0,0 +1,134 @@ +Running Sphinx v9.1.0 +loading translations [en]... done +making output directory... done +[AutoAPI] Reading files... [ 2%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\__init__.py [AutoAPI] Reading files... [ 4%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\client.py [AutoAPI] Reading files... [ 7%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\_skill_installer.py [AutoAPI] Reading files... [ 9%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\__init__.py [AutoAPI] Reading files... [ 11%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\claude_skill\__init__.py [AutoAPI] Reading files... [ 13%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\constants.py [AutoAPI] Reading files... [ 15%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\__init__.py [AutoAPI] Reading files... [ 17%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\config.py [AutoAPI] Reading files... [ 20%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\errors.py [AutoAPI] Reading files... [ 22%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\log_config.py [AutoAPI] Reading files... [ 24%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_auth.py [AutoAPI] Reading files... [ 26%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_error_codes.py [AutoAPI] Reading files... [ 28%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http.py [AutoAPI] Reading files... [ 30%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http_logger.py [AutoAPI] Reading files... [ 33%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\__init__.py [AutoAPI] Reading files... [ 35%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch.py [AutoAPI] Reading files... [ 37%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch_base.py [AutoAPI] Reading files... [ 39%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata.py [AutoAPI] Reading files... [ 41%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata_base.py [AutoAPI] Reading files... [ 43%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_raw_request.py [AutoAPI] Reading files... [ 46%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_relationships.py [AutoAPI] Reading files... [ 48%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_upload.py [AutoAPI] Reading files... [ 50%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\__init__.py [AutoAPI] Reading files... [ 52%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\extensions\__init__.py [AutoAPI] Reading files... [ 54%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\migrate_v0_to_v1.py [AutoAPI] Reading files... [ 57%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\__init__.py [AutoAPI] Reading files... [ 59%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\batch.py [AutoAPI] Reading files... [ 61%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\fetchxml_query.py [AutoAPI] Reading files... [ 63%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\filters.py [AutoAPI] Reading files... [ 65%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\labels.py [AutoAPI] Reading files... [ 67%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\protocol.py [AutoAPI] Reading files... [ 70%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\query_builder.py [AutoAPI] Reading files... [ 72%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\record.py [AutoAPI] Reading files... [ 74%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\relationship.py [AutoAPI] Reading files... [ 76%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\table_info.py [AutoAPI] Reading files... [ 78%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\upsert.py [AutoAPI] Reading files... [ 80%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\__init__.py [AutoAPI] Reading files... [ 83%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\batch.py [AutoAPI] Reading files... [ 85%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\dataframe.py [AutoAPI] Reading files... [ 87%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\files.py [AutoAPI] Reading files... [ 89%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\query.py [AutoAPI] Reading files... [ 91%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\records.py [AutoAPI] Reading files... [ 93%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\tables.py [AutoAPI] Reading files... [ 96%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\__init__.py [AutoAPI] Reading files... [ 98%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\_pandas.py [AutoAPI] Reading files... [100%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\__init__.py +[AutoAPI] Mapping Data... [ 2%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\__init__.py [AutoAPI] Mapping Data... [ 4%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\client.py [AutoAPI] Mapping Data... [ 7%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\_skill_installer.py [AutoAPI] Mapping Data... [ 9%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\__init__.py [AutoAPI] Mapping Data... [ 11%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\claude_skill\__init__.py [AutoAPI] Mapping Data... [ 13%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\constants.py [AutoAPI] Mapping Data... [ 15%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\__init__.py [AutoAPI] Mapping Data... [ 17%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\config.py [AutoAPI] Mapping Data... [ 20%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\errors.py [AutoAPI] Mapping Data... [ 22%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\log_config.py [AutoAPI] Mapping Data... [ 24%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_auth.py [AutoAPI] Mapping Data... [ 26%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_error_codes.py [AutoAPI] Mapping Data... [ 28%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http.py [AutoAPI] Mapping Data... [ 30%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http_logger.py [AutoAPI] Mapping Data... [ 33%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\__init__.py [AutoAPI] Mapping Data... [ 35%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch.py [AutoAPI] Mapping Data... [ 37%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch_base.py [AutoAPI] Mapping Data... [ 39%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata.py [AutoAPI] Mapping Data... [ 41%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata_base.py [AutoAPI] Mapping Data... [ 43%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_raw_request.py [AutoAPI] Mapping Data... [ 46%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_relationships.py [AutoAPI] Mapping Data... [ 48%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_upload.py [AutoAPI] Mapping Data... [ 50%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\__init__.py [AutoAPI] Mapping Data... [ 52%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\extensions\__init__.py [AutoAPI] Mapping Data... [ 54%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\migrate_v0_to_v1.py [AutoAPI] Mapping Data... [ 57%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\__init__.py [AutoAPI] Mapping Data... [ 59%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\batch.py [AutoAPI] Mapping Data... [ 61%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\fetchxml_query.py [AutoAPI] Mapping Data... [ 63%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\filters.py [AutoAPI] Mapping Data... [ 65%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\labels.py [AutoAPI] Mapping Data... [ 67%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\protocol.py [AutoAPI] Mapping Data... [ 70%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\query_builder.py [AutoAPI] Mapping Data... [ 72%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\record.py [AutoAPI] Mapping Data... [ 74%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\relationship.py [AutoAPI] Mapping Data... [ 76%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\table_info.py [AutoAPI] Mapping Data... [ 78%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\upsert.py [AutoAPI] Mapping Data... [ 80%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\__init__.py [AutoAPI] Mapping Data... [ 83%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\batch.py [AutoAPI] Mapping Data... [ 85%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\dataframe.py [AutoAPI] Mapping Data... [ 87%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\files.py [AutoAPI] Mapping Data... [ 89%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\query.py [AutoAPI] Mapping Data... [ 91%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\records.py [AutoAPI] Mapping Data... [ 93%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\tables.py [AutoAPI] Mapping Data... [ 96%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\__init__.py [AutoAPI] Mapping Data... [ 98%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\_pandas.py [AutoAPI] Mapping Data... [100%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\__init__.py +[AutoAPI] Rendering Data... [ 3%] PowerPlatform +[AutoAPI] Rendering Data... [ 6%] PowerPlatform.Dataverse +[AutoAPI] Rendering Data... [ 9%] PowerPlatform.Dataverse.core +[AutoAPI] Rendering Data... [ 12%] PowerPlatform.Dataverse.data +[AutoAPI] Rendering Data... [ 15%] PowerPlatform.Dataverse.utils +[AutoAPI] Rendering Data... [ 18%] PowerPlatform.Dataverse.client +[AutoAPI] Rendering Data... [ 21%] PowerPlatform.Dataverse.common +[AutoAPI] Rendering Data... [ 24%] PowerPlatform.Dataverse.models +[AutoAPI] Rendering Data... [ 27%] PowerPlatform.Dataverse.migration +[AutoAPI] Rendering Data... [ 30%] PowerPlatform.Dataverse.extensions +[AutoAPI] Rendering Data... [ 33%] PowerPlatform.Dataverse.operations +[AutoAPI] Rendering Data... [ 36%] PowerPlatform.Dataverse.core.config +[AutoAPI] Rendering Data... [ 39%] PowerPlatform.Dataverse.core.errors +[AutoAPI] Rendering Data... [ 42%] PowerPlatform.Dataverse.claude_skill +[AutoAPI] Rendering Data... [ 45%] PowerPlatform.Dataverse.models.batch +[AutoAPI] Rendering Data... [ 48%] PowerPlatform.Dataverse.models.labels +[AutoAPI] Rendering Data... [ 52%] PowerPlatform.Dataverse.models.record +[AutoAPI] Rendering Data... [ 55%] PowerPlatform.Dataverse.models.upsert +[AutoAPI] Rendering Data... [ 58%] PowerPlatform.Dataverse.models.filters +[AutoAPI] Rendering Data... [ 61%] PowerPlatform.Dataverse.core.log_config +[AutoAPI] Rendering Data... [ 64%] PowerPlatform.Dataverse.models.protocol +[AutoAPI] Rendering Data... [ 67%] PowerPlatform.Dataverse.common.constants +[AutoAPI] Rendering Data... [ 70%] PowerPlatform.Dataverse.operations.batch +[AutoAPI] Rendering Data... [ 73%] PowerPlatform.Dataverse.operations.files +[AutoAPI] Rendering Data... [ 76%] PowerPlatform.Dataverse.operations.query +[AutoAPI] Rendering Data... [ 79%] PowerPlatform.Dataverse.models.table_info +[AutoAPI] Rendering Data... [ 82%] PowerPlatform.Dataverse.operations.tables +[AutoAPI] Rendering Data... [ 85%] PowerPlatform.Dataverse.operations.records +[AutoAPI] Rendering Data... [ 88%] PowerPlatform.Dataverse.models.relationship +[AutoAPI] Rendering Data... [ 91%] PowerPlatform.Dataverse.models.query_builder +[AutoAPI] Rendering Data... [ 94%] PowerPlatform.Dataverse.operations.dataframe +[AutoAPI] Rendering Data... [ 97%] PowerPlatform.Dataverse.models.fetchxml_query +[AutoAPI] Rendering Data... [100%] PowerPlatform.Dataverse.migration.migrate_v0_to_v1 + +[autosummary] generating autosummary for: index.rst +building [mo]: targets for 0 po files that are out of date +writing output...  +building [html]: targets for 1 source files that are out of date +updating environment: [new config] 35 added, 0 changed, 0 removed +reading sources... [ 3%] autoapi/PowerPlatform/Dataverse/claude_skill/index reading sources... [ 6%] autoapi/PowerPlatform/Dataverse/client/index reading sources... [ 9%] autoapi/PowerPlatform/Dataverse/common/constants/index reading sources... [ 11%] autoapi/PowerPlatform/Dataverse/common/index reading sources... [ 14%] autoapi/PowerPlatform/Dataverse/core/config/index reading sources... [ 17%] autoapi/PowerPlatform/Dataverse/core/errors/index reading sources... [ 20%] autoapi/PowerPlatform/Dataverse/core/index reading sources... [ 23%] autoapi/PowerPlatform/Dataverse/core/log_config/index reading sources... [ 26%] autoapi/PowerPlatform/Dataverse/data/index reading sources... [ 29%] autoapi/PowerPlatform/Dataverse/extensions/index reading sources... [ 31%] autoapi/PowerPlatform/Dataverse/index reading sources... [ 34%] autoapi/PowerPlatform/Dataverse/migration/index reading sources... [ 37%] autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index reading sources... [ 40%] autoapi/PowerPlatform/Dataverse/models/batch/index reading sources... [ 43%] autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index reading sources... [ 46%] autoapi/PowerPlatform/Dataverse/models/filters/index reading sources... [ 49%] autoapi/PowerPlatform/Dataverse/models/index reading sources... [ 51%] autoapi/PowerPlatform/Dataverse/models/labels/index reading sources... [ 54%] autoapi/PowerPlatform/Dataverse/models/protocol/index reading sources... [ 57%] autoapi/PowerPlatform/Dataverse/models/query_builder/index reading sources... [ 60%] autoapi/PowerPlatform/Dataverse/models/record/index reading sources... [ 63%] autoapi/PowerPlatform/Dataverse/models/relationship/index reading sources... [ 66%] autoapi/PowerPlatform/Dataverse/models/table_info/index reading sources... [ 69%] autoapi/PowerPlatform/Dataverse/models/upsert/index reading sources... [ 71%] autoapi/PowerPlatform/Dataverse/operations/batch/index reading sources... [ 74%] autoapi/PowerPlatform/Dataverse/operations/dataframe/index reading sources... [ 77%] autoapi/PowerPlatform/Dataverse/operations/files/index reading sources... [ 80%] autoapi/PowerPlatform/Dataverse/operations/index reading sources... [ 83%] autoapi/PowerPlatform/Dataverse/operations/query/index reading sources... [ 86%] autoapi/PowerPlatform/Dataverse/operations/records/index reading sources... [ 89%] autoapi/PowerPlatform/Dataverse/operations/tables/index reading sources... [ 91%] autoapi/PowerPlatform/Dataverse/utils/index reading sources... [ 94%] autoapi/PowerPlatform/index reading sources... [ 97%] autoapi/index reading sources... [100%] index +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:77: ERROR: Unexpected indentation. [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:72: WARNING: Block quote ends without a blank line; unexpected unindent. [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:78: WARNING: Definition list ends without a blank line; unexpected unindent. [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\filters\index.rst:181: ERROR: Malformed table. +Right border not aligned or missing. + ++-----------------+-----------------------------+-------------------------------------+ +| Pattern form | Example | Compiles to | ++=================+=============================+=====================================+ +| ``val%`` | ``like("Contoso%")`` | ``startswith(column,'Contoso')`` | ++-----------------+-----------------------------+-------------------------------------+ +| ``%val`` | ``like("%Ltd")`` | ``endswith(column,'Ltd')`` | ++-----------------+-----------------------------+-------------------------------------+ +| ``%val%`` | ``like("%Corp%")`` | ``contains(column,'Corp')`` | ++-----------------+-----------------------------+-------------------------------------+ +| No wildcard | ``like("Contoso")`` | ``column eq 'Contoso'`` | ++-----------------+-----------------------------+-------------------------------------+ +| Other | ``like("Con%oso")`` | :class:`ValueError` | ++-----------------+-----------------------------+-------------------------------------+ [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\query\index.rst:114: WARNING: Inline emphasis start-string without end-string. [docutils] +looking for now-outdated files... none found +pickling environment... done +checking consistency... done +preparing documents... done +copying assets...  +copying static files...  +Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\basic.css +Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\documentation_options.js +Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\language_data.js +Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\alabaster.css +copying static files: done +copying extra files...  +copying extra files: done +copying assets: done +writing output... [ 3%] autoapi/PowerPlatform/Dataverse/claude_skill/index writing output... [ 6%] autoapi/PowerPlatform/Dataverse/client/index writing output... [ 9%] autoapi/PowerPlatform/Dataverse/common/constants/index writing output... [ 11%] autoapi/PowerPlatform/Dataverse/common/index writing output... [ 14%] autoapi/PowerPlatform/Dataverse/core/config/index writing output... [ 17%] autoapi/PowerPlatform/Dataverse/core/errors/index writing output... [ 20%] autoapi/PowerPlatform/Dataverse/core/index writing output... [ 23%] autoapi/PowerPlatform/Dataverse/core/log_config/index writing output... [ 26%] autoapi/PowerPlatform/Dataverse/data/index writing output... [ 29%] autoapi/PowerPlatform/Dataverse/extensions/index writing output... [ 31%] autoapi/PowerPlatform/Dataverse/index writing output... [ 34%] autoapi/PowerPlatform/Dataverse/migration/index writing output... [ 37%] autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index writing output... [ 40%] autoapi/PowerPlatform/Dataverse/models/batch/index writing output... [ 43%] autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index writing output... [ 46%] autoapi/PowerPlatform/Dataverse/models/filters/index writing output... [ 49%] autoapi/PowerPlatform/Dataverse/models/index writing output... [ 51%] autoapi/PowerPlatform/Dataverse/models/labels/index writing output... [ 54%] autoapi/PowerPlatform/Dataverse/models/protocol/index writing output... [ 57%] autoapi/PowerPlatform/Dataverse/models/query_builder/index writing output... [ 60%] autoapi/PowerPlatform/Dataverse/models/record/index writing output... [ 63%] autoapi/PowerPlatform/Dataverse/models/relationship/index writing output... [ 66%] autoapi/PowerPlatform/Dataverse/models/table_info/index writing output... [ 69%] autoapi/PowerPlatform/Dataverse/models/upsert/index writing output... [ 71%] autoapi/PowerPlatform/Dataverse/operations/batch/index writing output... [ 74%] autoapi/PowerPlatform/Dataverse/operations/dataframe/index writing output... [ 77%] autoapi/PowerPlatform/Dataverse/operations/files/index writing output... [ 80%] autoapi/PowerPlatform/Dataverse/operations/index writing output... [ 83%] autoapi/PowerPlatform/Dataverse/operations/query/index writing output... [ 86%] autoapi/PowerPlatform/Dataverse/operations/records/index writing output... [ 89%] autoapi/PowerPlatform/Dataverse/operations/tables/index writing output... [ 91%] autoapi/PowerPlatform/Dataverse/utils/index writing output... [ 94%] autoapi/PowerPlatform/index writing output... [ 97%] autoapi/index writing output... [100%] index +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\client\index.rst:18: WARNING: py:class reference target not found: azure.core.credentials.TokenCredential [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\client\index.rst:16: WARNING: py:class reference target not found: azure.core.credentials.TokenCredential [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:33::1: WARNING: py:class reference target not found: Record [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:42::1: WARNING: py:class reference target not found: ColumnProxy [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:48: WARNING: py:class reference target not found: ColumnProxy [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:61: WARNING: py:class reference target not found: ColumnProxy [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:73: WARNING: py:class reference target not found: FilterExpression [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:154: WARNING: py:class reference target not found: Record [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:162: WARNING: py:class reference target not found: Record [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:44: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:113: WARNING: py:class reference target not found: pathlib.Path [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:63::1: WARNING: py:meth reference target not found: QueryBuilder.build [ref.meth] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:72: WARNING: py:meth reference target not found: QueryBuilder.build [ref.meth] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:193: WARNING: py:obj reference target not found: _QueryBuilderBase [ref.obj] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:198: WARNING: py:meth reference target not found: build [ref.meth] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:229: WARNING: py:meth reference target not found: build [ref.meth] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:295: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\record\index.rst:22: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:30: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:93: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:114: WARNING: py:class reference target not found: _BatchContext [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:324: WARNING: py:class reference target not found: _BatchContext [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:508: WARNING: py:class reference target not found: _BatchContext [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:539: WARNING: py:class reference target not found: _BatchContext [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:563: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:585: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:617: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:54: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:93: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:154: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:154: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:189: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:248: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:: WARNING: py:class reference target not found: collections.abc.Iterable [ref.class] +generating indices... genindex py-modindex done +writing additional pages... search done +dumping search index in English (code: en)... done +dumping object inventory... done +build succeeded, 52 warnings. + +The HTML pages are in docs_local\_build. diff --git a/docs_local/build_post_restore.log b/docs_local/build_post_restore.log new file mode 100644 index 00000000..807500be --- /dev/null +++ b/docs_local/build_post_restore.log @@ -0,0 +1,187 @@ +Running Sphinx v9.1.0 +loading translations [en]... done +making output directory... done +[AutoAPI] Reading files... [ 2%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\__init__.py [AutoAPI] Reading files... [ 4%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\client.py [AutoAPI] Reading files... [ 7%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\_skill_installer.py [AutoAPI] Reading files... [ 9%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\__init__.py [AutoAPI] Reading files... [ 11%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\claude_skill\__init__.py [AutoAPI] Reading files... [ 13%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\constants.py [AutoAPI] Reading files... [ 15%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\__init__.py [AutoAPI] Reading files... [ 17%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\config.py [AutoAPI] Reading files... [ 20%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\errors.py [AutoAPI] Reading files... [ 22%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\log_config.py [AutoAPI] Reading files... [ 24%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_auth.py [AutoAPI] Reading files... [ 26%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_error_codes.py [AutoAPI] Reading files... [ 28%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http.py [AutoAPI] Reading files... [ 30%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http_logger.py [AutoAPI] Reading files... [ 33%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\__init__.py [AutoAPI] Reading files... [ 35%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch.py [AutoAPI] Reading files... [ 37%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch_base.py [AutoAPI] Reading files... [ 39%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata.py [AutoAPI] Reading files... [ 41%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata_base.py [AutoAPI] Reading files... [ 43%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_raw_request.py [AutoAPI] Reading files... [ 46%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_relationships.py [AutoAPI] Reading files... [ 48%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_upload.py [AutoAPI] Reading files... [ 50%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\__init__.py [AutoAPI] Reading files... [ 52%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\extensions\__init__.py [AutoAPI] Reading files... [ 54%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\migrate_v0_to_v1.py [AutoAPI] Reading files... [ 57%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\__init__.py [AutoAPI] Reading files... [ 59%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\batch.py [AutoAPI] Reading files... [ 61%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\fetchxml_query.py [AutoAPI] Reading files... [ 63%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\filters.py [AutoAPI] Reading files... [ 65%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\labels.py [AutoAPI] Reading files... [ 67%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\protocol.py [AutoAPI] Reading files... [ 70%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\query_builder.py [AutoAPI] Reading files... [ 72%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\record.py [AutoAPI] Reading files... [ 74%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\relationship.py [AutoAPI] Reading files... [ 76%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\table_info.py [AutoAPI] Reading files... [ 78%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\upsert.py [AutoAPI] Reading files... [ 80%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\__init__.py [AutoAPI] Reading files... [ 83%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\batch.py [AutoAPI] Reading files... [ 85%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\dataframe.py [AutoAPI] Reading files... [ 87%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\files.py [AutoAPI] Reading files... [ 89%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\query.py [AutoAPI] Reading files... [ 91%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\records.py [AutoAPI] Reading files... [ 93%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\tables.py [AutoAPI] Reading files... [ 96%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\__init__.py [AutoAPI] Reading files... [ 98%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\_pandas.py [AutoAPI] Reading files... [100%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\__init__.py +WARNING: Cannot resolve cyclic import: PowerPlatform.Dataverse.client, PowerPlatform.Dataverse.data._odata, PowerPlatform.Dataverse.data._odata_base, PowerPlatform.Dataverse, PowerPlatform.Dataverse.client [autoapi.python_import_resolution] +[AutoAPI] Mapping Data... [ 2%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\__init__.py [AutoAPI] Mapping Data... [ 4%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\client.py [AutoAPI] Mapping Data... [ 7%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\_skill_installer.py [AutoAPI] Mapping Data... [ 9%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\__init__.py [AutoAPI] Mapping Data... [ 11%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\claude_skill\__init__.py [AutoAPI] Mapping Data... [ 13%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\constants.py [AutoAPI] Mapping Data... [ 15%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\__init__.py [AutoAPI] Mapping Data... [ 17%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\config.py [AutoAPI] Mapping Data... [ 20%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\errors.py [AutoAPI] Mapping Data... [ 22%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\log_config.py [AutoAPI] Mapping Data... [ 24%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_auth.py [AutoAPI] Mapping Data... [ 26%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_error_codes.py [AutoAPI] Mapping Data... [ 28%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http.py [AutoAPI] Mapping Data... [ 30%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http_logger.py [AutoAPI] Mapping Data... [ 33%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\__init__.py [AutoAPI] Mapping Data... [ 35%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch.py [AutoAPI] Mapping Data... [ 37%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch_base.py [AutoAPI] Mapping Data... [ 39%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata.py [AutoAPI] Mapping Data... [ 41%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata_base.py [AutoAPI] Mapping Data... [ 43%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_raw_request.py [AutoAPI] Mapping Data... [ 46%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_relationships.py [AutoAPI] Mapping Data... [ 48%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_upload.py [AutoAPI] Mapping Data... [ 50%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\__init__.py [AutoAPI] Mapping Data... [ 52%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\extensions\__init__.py [AutoAPI] Mapping Data... [ 54%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\migrate_v0_to_v1.py [AutoAPI] Mapping Data... [ 57%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\__init__.py [AutoAPI] Mapping Data... [ 59%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\batch.py [AutoAPI] Mapping Data... [ 61%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\fetchxml_query.py [AutoAPI] Mapping Data... [ 63%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\filters.py [AutoAPI] Mapping Data... [ 65%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\labels.py [AutoAPI] Mapping Data... [ 67%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\protocol.py [AutoAPI] Mapping Data... [ 70%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\query_builder.py [AutoAPI] Mapping Data... [ 72%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\record.py [AutoAPI] Mapping Data... [ 74%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\relationship.py [AutoAPI] Mapping Data... [ 76%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\table_info.py [AutoAPI] Mapping Data... [ 78%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\upsert.py [AutoAPI] Mapping Data... [ 80%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\__init__.py [AutoAPI] Mapping Data... [ 83%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\batch.py [AutoAPI] Mapping Data... [ 85%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\dataframe.py [AutoAPI] Mapping Data... [ 87%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\files.py [AutoAPI] Mapping Data... [ 89%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\query.py [AutoAPI] Mapping Data... [ 91%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\records.py [AutoAPI] Mapping Data... [ 93%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\tables.py [AutoAPI] Mapping Data... [ 96%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\__init__.py [AutoAPI] Mapping Data... [ 98%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\_pandas.py [AutoAPI] Mapping Data... [100%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\__init__.py +[AutoAPI] Rendering Data... [ 3%] PowerPlatform +[AutoAPI] Rendering Data... [ 6%] PowerPlatform.Dataverse +[AutoAPI] Rendering Data... [ 9%] PowerPlatform.Dataverse.core +[AutoAPI] Rendering Data... [ 12%] PowerPlatform.Dataverse.data +[AutoAPI] Rendering Data... [ 15%] PowerPlatform.Dataverse.utils +[AutoAPI] Rendering Data... [ 18%] PowerPlatform.Dataverse.client +[AutoAPI] Rendering Data... [ 21%] PowerPlatform.Dataverse.common +[AutoAPI] Rendering Data... [ 24%] PowerPlatform.Dataverse.models +[AutoAPI] Rendering Data... [ 27%] PowerPlatform.Dataverse.migration +[AutoAPI] Rendering Data... [ 30%] PowerPlatform.Dataverse.extensions +[AutoAPI] Rendering Data... [ 33%] PowerPlatform.Dataverse.operations +[AutoAPI] Rendering Data... [ 36%] PowerPlatform.Dataverse.core.config +[AutoAPI] Rendering Data... [ 39%] PowerPlatform.Dataverse.core.errors +[AutoAPI] Rendering Data... [ 42%] PowerPlatform.Dataverse.claude_skill +[AutoAPI] Rendering Data... [ 45%] PowerPlatform.Dataverse.models.batch +[AutoAPI] Rendering Data... [ 48%] PowerPlatform.Dataverse.models.labels +[AutoAPI] Rendering Data... [ 52%] PowerPlatform.Dataverse.models.record +[AutoAPI] Rendering Data... [ 55%] PowerPlatform.Dataverse.models.upsert +[AutoAPI] Rendering Data... [ 58%] PowerPlatform.Dataverse.models.filters +[AutoAPI] Rendering Data... [ 61%] PowerPlatform.Dataverse.core.log_config +[AutoAPI] Rendering Data... [ 64%] PowerPlatform.Dataverse.models.protocol +[AutoAPI] Rendering Data... [ 67%] PowerPlatform.Dataverse.common.constants +[AutoAPI] Rendering Data... [ 70%] PowerPlatform.Dataverse.operations.batch +[AutoAPI] Rendering Data... [ 73%] PowerPlatform.Dataverse.operations.files +[AutoAPI] Rendering Data... [ 76%] PowerPlatform.Dataverse.operations.query +[AutoAPI] Rendering Data... [ 79%] PowerPlatform.Dataverse.models.table_info +[AutoAPI] Rendering Data... [ 82%] PowerPlatform.Dataverse.operations.tables +[AutoAPI] Rendering Data... [ 85%] PowerPlatform.Dataverse.operations.records +[AutoAPI] Rendering Data... [ 88%] PowerPlatform.Dataverse.models.relationship +[AutoAPI] Rendering Data... [ 91%] PowerPlatform.Dataverse.models.query_builder +[AutoAPI] Rendering Data... [ 94%] PowerPlatform.Dataverse.operations.dataframe +[AutoAPI] Rendering Data... [ 97%] PowerPlatform.Dataverse.models.fetchxml_query +[AutoAPI] Rendering Data... [100%] PowerPlatform.Dataverse.migration.migrate_v0_to_v1 + +[autosummary] generating autosummary for: index.rst +building [mo]: targets for 0 po files that are out of date +writing output...  +building [html]: targets for 1 source files that are out of date +updating environment: [new config] 35 added, 0 changed, 0 removed +reading sources... [ 3%] autoapi/PowerPlatform/Dataverse/claude_skill/index reading sources... [ 6%] autoapi/PowerPlatform/Dataverse/client/index reading sources... [ 9%] autoapi/PowerPlatform/Dataverse/common/constants/index reading sources... [ 11%] autoapi/PowerPlatform/Dataverse/common/index reading sources... [ 14%] autoapi/PowerPlatform/Dataverse/core/config/index reading sources... [ 17%] autoapi/PowerPlatform/Dataverse/core/errors/index reading sources... [ 20%] autoapi/PowerPlatform/Dataverse/core/index reading sources... [ 23%] autoapi/PowerPlatform/Dataverse/core/log_config/index reading sources... [ 26%] autoapi/PowerPlatform/Dataverse/data/index reading sources... [ 29%] autoapi/PowerPlatform/Dataverse/extensions/index reading sources... [ 31%] autoapi/PowerPlatform/Dataverse/index reading sources... [ 34%] autoapi/PowerPlatform/Dataverse/migration/index reading sources... [ 37%] autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index reading sources... [ 40%] autoapi/PowerPlatform/Dataverse/models/batch/index reading sources... [ 43%] autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index reading sources... [ 46%] autoapi/PowerPlatform/Dataverse/models/filters/index reading sources... [ 49%] autoapi/PowerPlatform/Dataverse/models/index reading sources... [ 51%] autoapi/PowerPlatform/Dataverse/models/labels/index reading sources... [ 54%] autoapi/PowerPlatform/Dataverse/models/protocol/index reading sources... [ 57%] autoapi/PowerPlatform/Dataverse/models/query_builder/index reading sources... [ 60%] autoapi/PowerPlatform/Dataverse/models/record/index reading sources... [ 63%] autoapi/PowerPlatform/Dataverse/models/relationship/index reading sources... [ 66%] autoapi/PowerPlatform/Dataverse/models/table_info/index reading sources... [ 69%] autoapi/PowerPlatform/Dataverse/models/upsert/index reading sources... [ 71%] autoapi/PowerPlatform/Dataverse/operations/batch/index reading sources... [ 74%] autoapi/PowerPlatform/Dataverse/operations/dataframe/index reading sources... [ 77%] autoapi/PowerPlatform/Dataverse/operations/files/index reading sources... [ 80%] autoapi/PowerPlatform/Dataverse/operations/index reading sources... [ 83%] autoapi/PowerPlatform/Dataverse/operations/query/index reading sources... [ 86%] autoapi/PowerPlatform/Dataverse/operations/records/index reading sources... [ 89%] autoapi/PowerPlatform/Dataverse/operations/tables/index reading sources... [ 91%] autoapi/PowerPlatform/Dataverse/utils/index reading sources... [ 94%] autoapi/PowerPlatform/index reading sources... [ 97%] autoapi/index reading sources... [100%] index +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:77: ERROR: Unexpected indentation. [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:72: WARNING: Block quote ends without a blank line; unexpected unindent. [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:78: WARNING: Definition list ends without a blank line; unexpected unindent. [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\filters\index.rst:181: ERROR: Malformed table. +Right border not aligned or missing. + ++-----------------+-----------------------------+-------------------------------------+ +| Pattern form | Example | Compiles to | ++=================+=============================+=====================================+ +| ``val%`` | ``like("Contoso%")`` | ``startswith(column,'Contoso')`` | ++-----------------+-----------------------------+-------------------------------------+ +| ``%val`` | ``like("%Ltd")`` | ``endswith(column,'Ltd')`` | ++-----------------+-----------------------------+-------------------------------------+ +| ``%val%`` | ``like("%Corp%")`` | ``contains(column,'Corp')`` | ++-----------------+-----------------------------+-------------------------------------+ +| No wildcard | ``like("Contoso")`` | ``column eq 'Contoso'`` | ++-----------------+-----------------------------+-------------------------------------+ +| Other | ``like("Con%oso")`` | :class:`ValueError` | ++-----------------+-----------------------------+-------------------------------------+ [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\index.rst:354: ERROR: Malformed table. +Right border not aligned or missing. + ++-----------------+-----------------------------+-------------------------------------+ +| Pattern form | Example | Compiles to | ++=================+=============================+=====================================+ +| ``val%`` | ``like("Contoso%")`` | ``startswith(column,'Contoso')`` | ++-----------------+-----------------------------+-------------------------------------+ +| ``%val`` | ``like("%Ltd")`` | ``endswith(column,'Ltd')`` | ++-----------------+-----------------------------+-------------------------------------+ +| ``%val%`` | ``like("%Corp%")`` | ``contains(column,'Corp')`` | ++-----------------+-----------------------------+-------------------------------------+ +| No wildcard | ``like("Contoso")`` | ``column eq 'Contoso'`` | ++-----------------+-----------------------------+-------------------------------------+ +| Other | ``like("Con%oso")`` | :class:`ValueError` | ++-----------------+-----------------------------+-------------------------------------+ [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:1189: WARNING: Inline emphasis start-string without end-string. [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\query\index.rst:114: WARNING: Inline emphasis start-string without end-string. [docutils] +looking for now-outdated files... none found +pickling environment... done +checking consistency... done +preparing documents... done +copying assets...  +copying static files...  +Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\basic.css +Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\documentation_options.js +Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\language_data.js +Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\alabaster.css +copying static files: done +copying extra files...  +copying extra files: done +copying assets: done +writing output... [ 3%] autoapi/PowerPlatform/Dataverse/claude_skill/index writing output... [ 6%] autoapi/PowerPlatform/Dataverse/client/index writing output... [ 9%] autoapi/PowerPlatform/Dataverse/common/constants/index writing output... [ 11%] autoapi/PowerPlatform/Dataverse/common/index writing output... [ 14%] autoapi/PowerPlatform/Dataverse/core/config/index writing output... [ 17%] autoapi/PowerPlatform/Dataverse/core/errors/index writing output... [ 20%] autoapi/PowerPlatform/Dataverse/core/index writing output... [ 23%] autoapi/PowerPlatform/Dataverse/core/log_config/index writing output... [ 26%] autoapi/PowerPlatform/Dataverse/data/index writing output... [ 29%] autoapi/PowerPlatform/Dataverse/extensions/index writing output... [ 31%] autoapi/PowerPlatform/Dataverse/index writing output... [ 34%] autoapi/PowerPlatform/Dataverse/migration/index writing output... [ 37%] autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index writing output... [ 40%] autoapi/PowerPlatform/Dataverse/models/batch/index writing output... [ 43%] autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index writing output... [ 46%] autoapi/PowerPlatform/Dataverse/models/filters/index writing output... [ 49%] autoapi/PowerPlatform/Dataverse/models/index writing output... [ 51%] autoapi/PowerPlatform/Dataverse/models/labels/index writing output... [ 54%] autoapi/PowerPlatform/Dataverse/models/protocol/index writing output... [ 57%] autoapi/PowerPlatform/Dataverse/models/query_builder/index writing output... [ 60%] autoapi/PowerPlatform/Dataverse/models/record/index writing output... [ 63%] autoapi/PowerPlatform/Dataverse/models/relationship/index writing output... [ 66%] autoapi/PowerPlatform/Dataverse/models/table_info/index writing output... [ 69%] autoapi/PowerPlatform/Dataverse/models/upsert/index writing output... [ 71%] autoapi/PowerPlatform/Dataverse/operations/batch/index writing output... [ 74%] autoapi/PowerPlatform/Dataverse/operations/dataframe/index writing output... [ 77%] autoapi/PowerPlatform/Dataverse/operations/files/index writing output... [ 80%] autoapi/PowerPlatform/Dataverse/operations/index writing output... [ 83%] autoapi/PowerPlatform/Dataverse/operations/query/index writing output... [ 86%] autoapi/PowerPlatform/Dataverse/operations/records/index writing output... [ 89%] autoapi/PowerPlatform/Dataverse/operations/tables/index writing output... [ 91%] autoapi/PowerPlatform/Dataverse/utils/index writing output... [ 94%] autoapi/PowerPlatform/index writing output... [ 97%] autoapi/index writing output... [100%] index +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\client\index.rst:18: WARNING: py:class reference target not found: azure.core.credentials.TokenCredential [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\client\index.rst:16: WARNING: py:class reference target not found: azure.core.credentials.TokenCredential [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:113: WARNING: py:class reference target not found: pathlib.Path [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\index.rst:671: WARNING: py:obj reference target not found: _QueryBuilderBase [ref.obj] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\index.rst:773: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\index.rst:81: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:193: WARNING: py:obj reference target not found: _QueryBuilderBase [ref.obj] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.models.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.models.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.models.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:295: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\record\index.rst:22: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\relationship\index.rst:25: WARNING: more than one target found for cross-reference 'Label': PowerPlatform.Dataverse.models.Label, PowerPlatform.Dataverse.models.labels.Label [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\relationship\index.rst:25: WARNING: more than one target found for cross-reference 'Label': PowerPlatform.Dataverse.models.Label, PowerPlatform.Dataverse.models.labels.Label [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:30: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:93: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:114: WARNING: py:class reference target not found: _BatchContext [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:275: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:324: WARNING: py:class reference target not found: _BatchContext [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:508: WARNING: py:class reference target not found: _BatchContext [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:539: WARNING: py:class reference target not found: _BatchContext [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:563: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:585: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:617: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:28: WARNING: more than one target found for cross-reference 'ValidationError': PowerPlatform.Dataverse.core.errors.ValidationError, PowerPlatform.Dataverse.core.ValidationError [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:28: WARNING: more than one target found for cross-reference 'MetadataError': PowerPlatform.Dataverse.core.errors.MetadataError, PowerPlatform.Dataverse.core.MetadataError [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:28: WARNING: more than one target found for cross-reference 'HttpError': PowerPlatform.Dataverse.core.errors.HttpError, PowerPlatform.Dataverse.core.HttpError [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:54: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:93: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:154: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:154: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:189: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:248: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:52: WARNING: py:class reference target not found: _BatchContext [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:76: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:98: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:130: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:176: WARNING: py:class reference target not found: _BatchContext [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:207: WARNING: py:class reference target not found: _BatchContext [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:368: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:50: WARNING: more than one target found for cross-reference 'ValidationError': PowerPlatform.Dataverse.core.errors.ValidationError, PowerPlatform.Dataverse.core.ValidationError [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:50: WARNING: more than one target found for cross-reference 'MetadataError': PowerPlatform.Dataverse.core.errors.MetadataError, PowerPlatform.Dataverse.core.MetadataError [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:50: WARNING: more than one target found for cross-reference 'HttpError': PowerPlatform.Dataverse.core.errors.HttpError, PowerPlatform.Dataverse.core.HttpError [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:496: WARNING: py:class reference target not found: _BatchContext [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:680: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:701: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:795: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:834: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:895: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:895: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:930: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:989: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: collections.abc.Iterable [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: more than one target found for cross-reference 'UpsertItem': PowerPlatform.Dataverse.models.UpsertItem, PowerPlatform.Dataverse.models.upsert.UpsertItem [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:: WARNING: py:class reference target not found: collections.abc.Iterable [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:: WARNING: more than one target found for cross-reference 'UpsertItem': PowerPlatform.Dataverse.models.UpsertItem, PowerPlatform.Dataverse.models.upsert.UpsertItem [ref.python] +generating indices... genindex py-modindex done +writing additional pages... search done +dumping search index in English (code: en)... done +dumping object inventory... done +build succeeded, 90 warnings. + +The HTML pages are in docs_local\_build. diff --git a/docs_local/build_review.log b/docs_local/build_review.log new file mode 100644 index 00000000..e3b19bf7 --- /dev/null +++ b/docs_local/build_review.log @@ -0,0 +1,120 @@ +Running Sphinx v9.1.0 +loading translations [en]... done +making output directory... done +[AutoAPI] Reading files... [ 2%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\__init__.py [AutoAPI] Reading files... [ 4%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\client.py [AutoAPI] Reading files... [ 7%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\_skill_installer.py [AutoAPI] Reading files... [ 9%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\__init__.py [AutoAPI] Reading files... [ 11%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\claude_skill\__init__.py [AutoAPI] Reading files... [ 13%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\constants.py [AutoAPI] Reading files... [ 15%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\__init__.py [AutoAPI] Reading files... [ 17%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\config.py [AutoAPI] Reading files... [ 20%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\errors.py [AutoAPI] Reading files... [ 22%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\log_config.py [AutoAPI] Reading files... [ 24%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_auth.py [AutoAPI] Reading files... [ 26%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_error_codes.py [AutoAPI] Reading files... [ 28%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http.py [AutoAPI] Reading files... [ 30%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http_logger.py [AutoAPI] Reading files... [ 33%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\__init__.py [AutoAPI] Reading files... [ 35%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch.py [AutoAPI] Reading files... [ 37%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch_base.py [AutoAPI] Reading files... [ 39%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata.py [AutoAPI] Reading files... [ 41%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata_base.py [AutoAPI] Reading files... [ 43%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_raw_request.py [AutoAPI] Reading files... [ 46%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_relationships.py [AutoAPI] Reading files... [ 48%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_upload.py [AutoAPI] Reading files... [ 50%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\__init__.py [AutoAPI] Reading files... [ 52%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\extensions\__init__.py [AutoAPI] Reading files... [ 54%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\migrate_v0_to_v1.py [AutoAPI] Reading files... [ 57%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\__init__.py [AutoAPI] Reading files... [ 59%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\batch.py [AutoAPI] Reading files... [ 61%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\fetchxml_query.py [AutoAPI] Reading files... [ 63%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\filters.py [AutoAPI] Reading files... [ 65%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\labels.py [AutoAPI] Reading files... [ 67%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\protocol.py [AutoAPI] Reading files... [ 70%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\query_builder.py [AutoAPI] Reading files... [ 72%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\record.py [AutoAPI] Reading files... [ 74%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\relationship.py [AutoAPI] Reading files... [ 76%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\table_info.py [AutoAPI] Reading files... [ 78%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\upsert.py [AutoAPI] Reading files... [ 80%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\__init__.py [AutoAPI] Reading files... [ 83%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\batch.py [AutoAPI] Reading files... [ 85%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\dataframe.py [AutoAPI] Reading files... [ 87%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\files.py [AutoAPI] Reading files... [ 89%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\query.py [AutoAPI] Reading files... [ 91%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\records.py [AutoAPI] Reading files... [ 93%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\tables.py [AutoAPI] Reading files... [ 96%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\__init__.py [AutoAPI] Reading files... [ 98%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\_pandas.py [AutoAPI] Reading files... [100%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\__init__.py +WARNING: Cannot resolve cyclic import: PowerPlatform.Dataverse.client, PowerPlatform.Dataverse.data._odata, PowerPlatform.Dataverse.data._odata_base, PowerPlatform.Dataverse, PowerPlatform.Dataverse.client [autoapi.python_import_resolution] +[AutoAPI] Mapping Data... [ 2%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\__init__.py [AutoAPI] Mapping Data... [ 4%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\client.py [AutoAPI] Mapping Data... [ 7%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\_skill_installer.py [AutoAPI] Mapping Data... [ 9%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\__init__.py [AutoAPI] Mapping Data... [ 11%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\claude_skill\__init__.py [AutoAPI] Mapping Data... [ 13%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\constants.py [AutoAPI] Mapping Data... [ 15%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\__init__.py [AutoAPI] Mapping Data... [ 17%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\config.py [AutoAPI] Mapping Data... [ 20%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\errors.py [AutoAPI] Mapping Data... [ 22%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\log_config.py [AutoAPI] Mapping Data... [ 24%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_auth.py [AutoAPI] Mapping Data... [ 26%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_error_codes.py [AutoAPI] Mapping Data... [ 28%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http.py [AutoAPI] Mapping Data... [ 30%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http_logger.py [AutoAPI] Mapping Data... [ 33%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\__init__.py [AutoAPI] Mapping Data... [ 35%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch.py [AutoAPI] Mapping Data... [ 37%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch_base.py [AutoAPI] Mapping Data... [ 39%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata.py [AutoAPI] Mapping Data... [ 41%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata_base.py [AutoAPI] Mapping Data... [ 43%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_raw_request.py [AutoAPI] Mapping Data... [ 46%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_relationships.py [AutoAPI] Mapping Data... [ 48%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_upload.py [AutoAPI] Mapping Data... [ 50%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\__init__.py [AutoAPI] Mapping Data... [ 52%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\extensions\__init__.py [AutoAPI] Mapping Data... [ 54%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\migrate_v0_to_v1.py [AutoAPI] Mapping Data... [ 57%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\__init__.py [AutoAPI] Mapping Data... [ 59%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\batch.py [AutoAPI] Mapping Data... [ 61%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\fetchxml_query.py [AutoAPI] Mapping Data... [ 63%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\filters.py [AutoAPI] Mapping Data... [ 65%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\labels.py [AutoAPI] Mapping Data... [ 67%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\protocol.py [AutoAPI] Mapping Data... [ 70%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\query_builder.py [AutoAPI] Mapping Data... [ 72%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\record.py [AutoAPI] Mapping Data... [ 74%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\relationship.py [AutoAPI] Mapping Data... [ 76%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\table_info.py [AutoAPI] Mapping Data... [ 78%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\upsert.py [AutoAPI] Mapping Data... [ 80%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\__init__.py [AutoAPI] Mapping Data... [ 83%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\batch.py [AutoAPI] Mapping Data... [ 85%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\dataframe.py [AutoAPI] Mapping Data... [ 87%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\files.py [AutoAPI] Mapping Data... [ 89%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\query.py [AutoAPI] Mapping Data... [ 91%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\records.py [AutoAPI] Mapping Data... [ 93%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\tables.py [AutoAPI] Mapping Data... [ 96%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\__init__.py [AutoAPI] Mapping Data... [ 98%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\_pandas.py [AutoAPI] Mapping Data... [100%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\__init__.py +[AutoAPI] Rendering Data... [ 3%] PowerPlatform +[AutoAPI] Rendering Data... [ 6%] PowerPlatform.Dataverse +[AutoAPI] Rendering Data... [ 9%] PowerPlatform.Dataverse.core +[AutoAPI] Rendering Data... [ 12%] PowerPlatform.Dataverse.data +[AutoAPI] Rendering Data... [ 15%] PowerPlatform.Dataverse.utils +[AutoAPI] Rendering Data... [ 18%] PowerPlatform.Dataverse.client +[AutoAPI] Rendering Data... [ 21%] PowerPlatform.Dataverse.common +[AutoAPI] Rendering Data... [ 24%] PowerPlatform.Dataverse.models +[AutoAPI] Rendering Data... [ 27%] PowerPlatform.Dataverse.migration +[AutoAPI] Rendering Data... [ 30%] PowerPlatform.Dataverse.extensions +[AutoAPI] Rendering Data... [ 33%] PowerPlatform.Dataverse.operations +[AutoAPI] Rendering Data... [ 36%] PowerPlatform.Dataverse.core.config +[AutoAPI] Rendering Data... [ 39%] PowerPlatform.Dataverse.core.errors +[AutoAPI] Rendering Data... [ 42%] PowerPlatform.Dataverse.claude_skill +[AutoAPI] Rendering Data... [ 45%] PowerPlatform.Dataverse.models.batch +[AutoAPI] Rendering Data... [ 48%] PowerPlatform.Dataverse.models.labels +[AutoAPI] Rendering Data... [ 52%] PowerPlatform.Dataverse.models.record +[AutoAPI] Rendering Data... [ 55%] PowerPlatform.Dataverse.models.upsert +[AutoAPI] Rendering Data... [ 58%] PowerPlatform.Dataverse.models.filters +[AutoAPI] Rendering Data... [ 61%] PowerPlatform.Dataverse.core.log_config +[AutoAPI] Rendering Data... [ 64%] PowerPlatform.Dataverse.models.protocol +[AutoAPI] Rendering Data... [ 67%] PowerPlatform.Dataverse.common.constants +[AutoAPI] Rendering Data... [ 70%] PowerPlatform.Dataverse.operations.batch +[AutoAPI] Rendering Data... [ 73%] PowerPlatform.Dataverse.operations.files +[AutoAPI] Rendering Data... [ 76%] PowerPlatform.Dataverse.operations.query +[AutoAPI] Rendering Data... [ 79%] PowerPlatform.Dataverse.models.table_info +[AutoAPI] Rendering Data... [ 82%] PowerPlatform.Dataverse.operations.tables +[AutoAPI] Rendering Data... [ 85%] PowerPlatform.Dataverse.operations.records +[AutoAPI] Rendering Data... [ 88%] PowerPlatform.Dataverse.models.relationship +[AutoAPI] Rendering Data... [ 91%] PowerPlatform.Dataverse.models.query_builder +[AutoAPI] Rendering Data... [ 94%] PowerPlatform.Dataverse.operations.dataframe +[AutoAPI] Rendering Data... [ 97%] PowerPlatform.Dataverse.models.fetchxml_query +[AutoAPI] Rendering Data... [100%] PowerPlatform.Dataverse.migration.migrate_v0_to_v1 + +[autosummary] generating autosummary for: index.rst +building [mo]: targets for 0 po files that are out of date +writing output...  +building [html]: targets for 1 source files that are out of date +updating environment: [new config] 35 added, 0 changed, 0 removed +reading sources... [ 3%] autoapi/PowerPlatform/Dataverse/claude_skill/index reading sources... [ 6%] autoapi/PowerPlatform/Dataverse/client/index reading sources... [ 9%] autoapi/PowerPlatform/Dataverse/common/constants/index reading sources... [ 11%] autoapi/PowerPlatform/Dataverse/common/index reading sources... [ 14%] autoapi/PowerPlatform/Dataverse/core/config/index reading sources... [ 17%] autoapi/PowerPlatform/Dataverse/core/errors/index reading sources... [ 20%] autoapi/PowerPlatform/Dataverse/core/index reading sources... [ 23%] autoapi/PowerPlatform/Dataverse/core/log_config/index reading sources... [ 26%] autoapi/PowerPlatform/Dataverse/data/index reading sources... [ 29%] autoapi/PowerPlatform/Dataverse/extensions/index reading sources... [ 31%] autoapi/PowerPlatform/Dataverse/index reading sources... [ 34%] autoapi/PowerPlatform/Dataverse/migration/index reading sources... [ 37%] autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index reading sources... [ 40%] autoapi/PowerPlatform/Dataverse/models/batch/index reading sources... [ 43%] autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index reading sources... [ 46%] autoapi/PowerPlatform/Dataverse/models/filters/index reading sources... [ 49%] autoapi/PowerPlatform/Dataverse/models/index reading sources... [ 51%] autoapi/PowerPlatform/Dataverse/models/labels/index reading sources... [ 54%] autoapi/PowerPlatform/Dataverse/models/protocol/index reading sources... [ 57%] autoapi/PowerPlatform/Dataverse/models/query_builder/index reading sources... [ 60%] autoapi/PowerPlatform/Dataverse/models/record/index reading sources... [ 63%] autoapi/PowerPlatform/Dataverse/models/relationship/index reading sources... [ 66%] autoapi/PowerPlatform/Dataverse/models/table_info/index reading sources... [ 69%] autoapi/PowerPlatform/Dataverse/models/upsert/index reading sources... [ 71%] autoapi/PowerPlatform/Dataverse/operations/batch/index reading sources... [ 74%] autoapi/PowerPlatform/Dataverse/operations/dataframe/index reading sources... [ 77%] autoapi/PowerPlatform/Dataverse/operations/files/index reading sources... [ 80%] autoapi/PowerPlatform/Dataverse/operations/index reading sources... [ 83%] autoapi/PowerPlatform/Dataverse/operations/query/index reading sources... [ 86%] autoapi/PowerPlatform/Dataverse/operations/records/index reading sources... [ 89%] autoapi/PowerPlatform/Dataverse/operations/tables/index reading sources... [ 91%] autoapi/PowerPlatform/Dataverse/utils/index reading sources... [ 94%] autoapi/PowerPlatform/index reading sources... [ 97%] autoapi/index reading sources... [100%] index +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:77: ERROR: Unexpected indentation. [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:72: WARNING: Block quote ends without a blank line; unexpected unindent. [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:78: WARNING: Definition list ends without a blank line; unexpected unindent. [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\filters\index.rst:181: ERROR: Malformed table. +Right border not aligned or missing. + ++-----------------+-----------------------------+-------------------------------------+ +| Pattern form | Example | Compiles to | ++=================+=============================+=====================================+ +| ``val%`` | ``like("Contoso%")`` | ``startswith(column,'Contoso')`` | ++-----------------+-----------------------------+-------------------------------------+ +| ``%val`` | ``like("%Ltd")`` | ``endswith(column,'Ltd')`` | ++-----------------+-----------------------------+-------------------------------------+ +| ``%val%`` | ``like("%Corp%")`` | ``contains(column,'Corp')`` | ++-----------------+-----------------------------+-------------------------------------+ +| No wildcard | ``like("Contoso")`` | ``column eq 'Contoso'`` | ++-----------------+-----------------------------+-------------------------------------+ +| Other | ``like("Con%oso")`` | :class:`ValueError` | ++-----------------+-----------------------------+-------------------------------------+ [docutils] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\query\index.rst:114: WARNING: Inline emphasis start-string without end-string. [docutils] +looking for now-outdated files... none found +pickling environment... done +checking consistency... done +preparing documents... done +copying assets...  +copying static files...  +Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\basic.css +Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\documentation_options.js +Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\language_data.js +Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\alabaster.css +copying static files: done +copying extra files...  +copying extra files: done +copying assets: done +writing output... [ 3%] autoapi/PowerPlatform/Dataverse/claude_skill/index writing output... [ 6%] autoapi/PowerPlatform/Dataverse/client/index writing output... [ 9%] autoapi/PowerPlatform/Dataverse/common/constants/index writing output... [ 11%] autoapi/PowerPlatform/Dataverse/common/index writing output... [ 14%] autoapi/PowerPlatform/Dataverse/core/config/index writing output... [ 17%] autoapi/PowerPlatform/Dataverse/core/errors/index writing output... [ 20%] autoapi/PowerPlatform/Dataverse/core/index writing output... [ 23%] autoapi/PowerPlatform/Dataverse/core/log_config/index writing output... [ 26%] autoapi/PowerPlatform/Dataverse/data/index writing output... [ 29%] autoapi/PowerPlatform/Dataverse/extensions/index writing output... [ 31%] autoapi/PowerPlatform/Dataverse/index writing output... [ 34%] autoapi/PowerPlatform/Dataverse/migration/index writing output... [ 37%] autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index writing output... [ 40%] autoapi/PowerPlatform/Dataverse/models/batch/index writing output... [ 43%] autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index writing output... [ 46%] autoapi/PowerPlatform/Dataverse/models/filters/index writing output... [ 49%] autoapi/PowerPlatform/Dataverse/models/index writing output... [ 51%] autoapi/PowerPlatform/Dataverse/models/labels/index writing output... [ 54%] autoapi/PowerPlatform/Dataverse/models/protocol/index writing output... [ 57%] autoapi/PowerPlatform/Dataverse/models/query_builder/index writing output... [ 60%] autoapi/PowerPlatform/Dataverse/models/record/index writing output... [ 63%] autoapi/PowerPlatform/Dataverse/models/relationship/index writing output... [ 66%] autoapi/PowerPlatform/Dataverse/models/table_info/index writing output... [ 69%] autoapi/PowerPlatform/Dataverse/models/upsert/index writing output... [ 71%] autoapi/PowerPlatform/Dataverse/operations/batch/index writing output... [ 74%] autoapi/PowerPlatform/Dataverse/operations/dataframe/index writing output... [ 77%] autoapi/PowerPlatform/Dataverse/operations/files/index writing output... [ 80%] autoapi/PowerPlatform/Dataverse/operations/index writing output... [ 83%] autoapi/PowerPlatform/Dataverse/operations/query/index writing output... [ 86%] autoapi/PowerPlatform/Dataverse/operations/records/index writing output... [ 89%] autoapi/PowerPlatform/Dataverse/operations/tables/index writing output... [ 91%] autoapi/PowerPlatform/Dataverse/utils/index writing output... [ 94%] autoapi/PowerPlatform/index writing output... [ 97%] autoapi/index writing output... [100%] index +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\client\index.rst:18: WARNING: py:class reference target not found: azure.core.credentials.TokenCredential [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\client\index.rst:16: WARNING: py:class reference target not found: azure.core.credentials.TokenCredential [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:113: WARNING: py:class reference target not found: pathlib.Path [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:193: WARNING: py:obj reference target not found: _QueryBuilderBase [ref.obj] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:295: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\record\index.rst:22: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:30: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:93: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:114: WARNING: py:class reference target not found: _BatchContext [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:324: WARNING: py:class reference target not found: _BatchContext [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:508: WARNING: py:class reference target not found: _BatchContext [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:539: WARNING: py:class reference target not found: _BatchContext [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:563: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:585: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:617: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:54: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:93: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:154: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:154: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:189: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:248: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:: WARNING: py:class reference target not found: collections.abc.Iterable [ref.class] +generating indices... genindex py-modindex done +writing additional pages... search done +dumping search index in English (code: en)... done +dumping object inventory... done +build succeeded, 38 warnings. + +The HTML pages are in docs_local\_build. diff --git a/docs_local/conf.py b/docs_local/conf.py new file mode 100644 index 00000000..f9c861b2 --- /dev/null +++ b/docs_local/conf.py @@ -0,0 +1,41 @@ +"""Minimal Sphinx config to reproduce the doc-generation behavior locally.""" + +import os +import sys + +# Make the local source importable +sys.path.insert(0, os.path.abspath("../src")) + +project = "PowerPlatform-Dataverse-Client" +author = "Microsoft Corporation" +release = "0.1.0b11" + +extensions = [ + "sphinx.ext.autodoc", + "sphinx.ext.napoleon", + "autoapi.extension", +] + +# autoapi: auto-discover the package from source +autoapi_type = "python" +autoapi_dirs = ["../src/PowerPlatform"] +autoapi_options = [ + "members", + "undoc-members", + "show-inheritance", + "show-module-summary", + # 'imported-members' deliberately omitted: when re-exports are present in + # __all__ (as in our package __init__.py files), 'imported-members' causes + # autoapi to generate duplicate doc pages — one at the canonical module + # path and one at the re-export path. Without it, only the canonical page + # is generated and 'more than one target' warnings disappear. +] +autoapi_keep_files = True +autoapi_python_class_content = "both" + +# Make warnings visible and counted +nitpicky = True +suppress_warnings = [] + +html_theme = "alabaster" +exclude_patterns = ["_build"] diff --git a/docs_local/index.rst b/docs_local/index.rst new file mode 100644 index 00000000..a3445f40 --- /dev/null +++ b/docs_local/index.rst @@ -0,0 +1,8 @@ +PowerPlatform Dataverse Client SDK +=================================== + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + autoapi/index diff --git a/examples/advanced/memo_walkthrough.py b/examples/advanced/memo_walkthrough.py new file mode 100644 index 00000000..1254e60f --- /dev/null +++ b/examples/advanced/memo_walkthrough.py @@ -0,0 +1,426 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +""" +Walkthrough demonstrating memo/multiline column type. + +This example exercises memo columns comprehensively: +- Create table with memo column +- Create record with multiline text (newlines) +- Read back and verify multiline text preserved +- Update memo with new multiline content +- Empty string memo +- None/null memo +- Long text (near max length) +- Memo with special characters (quotes, unicode, tabs) +- Memo alongside picklist and other field types +- Verify memo is not mistaken for picklist label + +Prerequisites: +- pip install PowerPlatform-Dataverse-Client +- pip install azure-identity +""" + +import sys +import time +from enum import IntEnum +from azure.identity import InteractiveBrowserCredential +from PowerPlatform.Dataverse.client import DataverseClient +from PowerPlatform.Dataverse.core.errors import MetadataError +import requests + + +def log_call(description): + print(f"\n-> {description}") + + +class Priority(IntEnum): + LOW = 1 + MEDIUM = 2 + HIGH = 3 + + +def backoff(op, *, delays=(0, 2, 5, 10, 20, 20)): + last = None + total_delay = 0 + attempts = 0 + for d in delays: + if d: + time.sleep(d) + total_delay += d + attempts += 1 + try: + result = op() + if attempts > 1: + print(f" [INFO] Backoff succeeded after {attempts - 1} retry(s); waited {total_delay}s total.") + return result + except Exception as ex: + last = ex + continue + if last: + if attempts: + print(f" [WARN] Backoff exhausted after {max(attempts - 1, 0)} retry(s); waited {total_delay}s total.") + raise last + + +assertions_passed = [] + + +def check(label, actual, expected): + if actual == expected: + assertions_passed.append(label) + print(f" [OK] {label}") + else: + print(f" [FAIL] {label}") + print(f" Expected: {expected!r}") + print(f" Actual: {actual!r}") + + +def main(): + print("=" * 80) + print("Memo/Multiline Column Walkthrough") + print("=" * 80) + + print("\n" + "=" * 80) + print("1. Setup & Authentication") + print("=" * 80) + + base_url = sys.argv[1] if len(sys.argv) > 1 else input( + "Enter Dataverse org URL (e.g. https://yourorg.crm.dynamics.com): " + ).strip() + if not base_url: + print("No URL entered; exiting.") + sys.exit(1) + base_url = base_url.rstrip("/") + + log_call("InteractiveBrowserCredential()") + credential = InteractiveBrowserCredential() + + log_call(f"DataverseClient(base_url='{base_url}', credential=...)") + with DataverseClient(base_url=base_url, credential=credential) as client: + print(f"[OK] Connected to: {base_url}") + _run_walkthrough(client) + + +def _run_walkthrough(client): + table_name = "new_MemoDemo" + created_ids = [] + + # ============================================================================ + # 2. Table Creation + # ============================================================================ + print("\n" + "=" * 80) + print("2. Table Creation (memo + string + picklist)") + print("=" * 80) + + log_call(f"client.tables.get('{table_name}')") + table_info = backoff(lambda: client.tables.get(table_name)) + if table_info: + print(f"[OK] Table already exists, deleting for clean test...") + backoff(lambda: client.tables.delete(table_name)) + print(f"[OK] Deleted existing table") + time.sleep(5) + + log_call(f"client.tables.create('{table_name}', columns={{...}})") + columns = { + "new_Title": "string", + "new_Description": "memo", + "new_Priority": Priority, + } + table_info = backoff(lambda: client.tables.create(table_name, columns)) + print(f"[OK] Created table: {table_info.get('table_schema_name')}") + print(f" Columns: {', '.join(table_info.get('columns_created', []))}") + + # ============================================================================ + # 3. Basic Multiline Create & Read + # ============================================================================ + print("\n" + "=" * 80) + print("3. Basic Multiline Create & Read") + print("=" * 80) + + multiline_text = ( + "Subject: Quarterly Performance Review - Q1 2026\n" + "\n" + "Team,\n" + "\n" + "Below is a summary of our Q1 performance across key metrics:\n" + "\n" + "Revenue: $2.4M (up 12% from Q4)\n" + "New customers: 147 (target was 120)\n" + "Customer retention: 94.2%\n" + "Support ticket resolution: avg 4.2 hours\n" + "\n" + "Key highlights:\n" + "- Launched the Python SDK for Dataverse, receiving positive feedback\n" + " from early adopters in the data science community.\n" + "- Reduced API latency by 35% through connection pooling and\n" + " metadata caching optimizations.\n" + "- Onboarded 3 enterprise customers with 10K+ seat deployments.\n" + "\n" + "Areas for improvement:\n" + "- Documentation coverage needs to reach 90% before GA.\n" + "- Integration test suite completion is at 78% (target: 95%).\n" + "- Need to finalize the memo/multiline column support.\n" + "\n" + "Next steps: Schedule individual 1:1s to discuss Q2 goals.\n" + "\n" + "Best regards,\n" + "Engineering Lead" + ) + log_call("client.records.create(...) with multiline memo") + id1 = backoff(lambda: client.records.create(table_name, { + "new_Title": "Basic multiline test", + "new_Description": multiline_text, + "new_Priority": Priority.MEDIUM, + })) + created_ids.append(id1) + print(f"[OK] Created record: {id1}") + + record = backoff(lambda: client.records.get(table_name, id1)) + check("Multiline text preserved on create", + record.get("new_description"), multiline_text) + print(f"\n Stored memo ({len(record.get('new_description', ''))} chars):") + print(" " + "-" * 60) + for line in record.get("new_description", "").split("\n"): + print(f" | {line}") + print(" " + "-" * 60) + + # ============================================================================ + # 4. Update Memo with New Content + # ============================================================================ + print("\n" + "=" * 80) + print("4. Update Memo with New Content") + print("=" * 80) + + updated_text = ( + "UPDATED: Quarterly Performance Review - Q1 2026\n" + "\n" + "Revision notes: Added final numbers after audit.\n" + "\n" + "Revenue: $2.45M (revised up from $2.4M after late invoices)\n" + "New customers: 152 (revised up - 5 deals closed on Mar 31)\n" + "Customer retention: 94.2% (unchanged)\n" + "Support ticket resolution: avg 3.8 hours (improved from 4.2)\n" + "\n" + "Additional Q1 accomplishments:\n" + "- Shipped picklist label resolution optimization (92x faster at scale)\n" + "- Completed memo/multiline column support with full test coverage\n" + "- Published SDK to PyPI with 1,200+ downloads in first week\n" + "\n" + "Q2 priorities:\n" + "1. GA release preparation\n" + "2. Performance benchmarking framework\n" + "3. Expanded relationship management APIs\n" + "\n" + "-- Updated by Engineering Lead, April 2026" + ) + log_call("client.records.update(...) with new multiline memo") + backoff(lambda: client.records.update(table_name, id1, { + "new_Description": updated_text, + })) + record = backoff(lambda: client.records.get(table_name, id1)) + check("Multiline text preserved on update", + record.get("new_description"), updated_text) + + # ============================================================================ + # 5. Empty String Memo + # ============================================================================ + print("\n" + "=" * 80) + print("5. Empty String Memo") + print("=" * 80) + + log_call("client.records.create(...) with empty memo") + id2 = backoff(lambda: client.records.create(table_name, { + "new_Title": "Empty memo test", + "new_Description": "", + })) + created_ids.append(id2) + record = backoff(lambda: client.records.get(table_name, id2)) + # Dataverse may return None for empty strings + val = record.get("new_description") + is_empty = val is None or val == "" + if is_empty: + assertions_passed.append("Empty string stored as null/empty") + print(f" [OK] Empty string stored as null/empty (got: {val!r})") + else: + print(f" [FAIL] Expected null or empty, got: {val!r}") + + # ============================================================================ + # 6. None/Null Memo + # ============================================================================ + print("\n" + "=" * 80) + print("6. None/Null Memo") + print("=" * 80) + + log_call("client.records.create(...) with no memo field") + id3 = backoff(lambda: client.records.create(table_name, { + "new_Title": "No memo provided", + })) + created_ids.append(id3) + record = backoff(lambda: client.records.get(table_name, id3)) + check("Omitted memo field returns None", + record.get("new_description"), None) + + # ============================================================================ + # 7. Special Characters + # ============================================================================ + print("\n" + "=" * 80) + print("7. Special Characters") + print("=" * 80) + + special_text = ( + "Quotes: \"double\" and 'single'\n" + "Tabs:\there\tand\there\n" + "Unicode: cafe\u0301 \u2603 \u2764\n" + "Angle brackets: \n" + "Ampersand: A & B" + ) + log_call("client.records.create(...) with special characters") + id4 = backoff(lambda: client.records.create(table_name, { + "new_Title": "Special chars test", + "new_Description": special_text, + })) + created_ids.append(id4) + record = backoff(lambda: client.records.get(table_name, id4)) + check("Special characters preserved", + record.get("new_description"), special_text) + + # ============================================================================ + # 8. Long Text (near max length) + # ============================================================================ + print("\n" + "=" * 80) + print("8. Long Text (near max length)") + print("=" * 80) + + # MemoAttributeMetadata MaxLength is 4000 + long_text = "A" * 3900 + "\n" + "B" * 99 + log_call(f"client.records.create(...) with {len(long_text)} chars") + id5 = backoff(lambda: client.records.create(table_name, { + "new_Title": "Long text test", + "new_Description": long_text, + })) + created_ids.append(id5) + record = backoff(lambda: client.records.get(table_name, id5)) + stored = record.get("new_description") + check("Long text preserved (4000 chars)", + stored, long_text) + + # ============================================================================ + # 9. Memo Alongside Picklist (no interference) + # ============================================================================ + print("\n" + "=" * 80) + print("9. Memo Alongside Picklist (no interference)") + print("=" * 80) + + memo_with_picklist_label = "High" # Same as a picklist label + log_call("client.records.create(...) with memo='High' and picklist='Low'") + id6 = backoff(lambda: client.records.create(table_name, { + "new_Title": "Memo vs picklist test", + "new_Description": memo_with_picklist_label, + "new_Priority": "Low", + })) + created_ids.append(id6) + record = backoff(lambda: client.records.get(table_name, id6)) + check("Memo 'High' not resolved as picklist int", + record.get("new_description"), "High") + check("Picklist 'Low' resolved to int", + record.get("new_priority"), Priority.LOW) + + # ============================================================================ + # 10. Triple-Quoted String (Python multiline syntax) + # ============================================================================ + print("\n" + "=" * 80) + print("10. Triple-Quoted String (Python syntax)") + print("=" * 80) + + triple_text = """Dear Customer, + +Thank you for contacting Contoso Support regarding your recent order #12345. + +We have thoroughly reviewed your case and determined the following status +for each item in your order: + + Item A (Wireless Keyboard) - Shipped via FedEx, tracking #1Z999AA10123456784 + Expected delivery: April 5, 2026 + + Item B (USB-C Hub) - Currently on backorder due to supply chain delays. + Estimated availability: 2-3 weeks. We will ship immediately when + stock arrives and send you an updated tracking number. + + Item C (Screen Protector) - This item was damaged during fulfillment. + A full refund of $24.99 has been processed to your original payment + method. Please allow 5-7 business days for the credit to appear. + +If you would like to substitute Item B with an alternative product or +cancel that portion of your order, please reply to this message or +call us at 1-800-555-0199 (Mon-Fri, 8am-6pm PST). + +We sincerely apologize for any inconvenience and appreciate your +patience and continued business. + +Best regards, +Sarah Johnson +Customer Support Specialist +Contoso Corporation +Ref: CASE-2026-04-001""" + + log_call("client.records.create(...) with triple-quoted multiline") + id7 = backoff(lambda: client.records.create(table_name, { + "new_Title": "Triple-quoted test", + "new_Description": triple_text, + })) + created_ids.append(id7) + record = backoff(lambda: client.records.get(table_name, id7)) + check("Triple-quoted multiline preserved", + record.get("new_description"), triple_text) + + # ============================================================================ + # 11. Update Memo to None (clear field) + # ============================================================================ + print("\n" + "=" * 80) + print("11. Update Memo to None (clear field)") + print("=" * 80) + + log_call("client.records.update(...) set memo to None") + backoff(lambda: client.records.update(table_name, id1, { + "new_Description": None, + })) + record = backoff(lambda: client.records.get(table_name, id1)) + check("Memo cleared to None", + record.get("new_description"), None) + + # ============================================================================ + # 12. Cleanup + # ============================================================================ + print("\n" + "=" * 80) + print("12. Cleanup") + print("=" * 80) + + log_call(f"client.records.delete('{table_name}', [{len(created_ids)} IDs])") + backoff(lambda: client.records.delete(table_name, created_ids)) + print(f"[OK] Deleted {len(created_ids)} records") + + log_call(f"client.tables.delete('{table_name}')") + try: + backoff(lambda: client.tables.delete(table_name)) + print(f"[OK] Deleted table: {table_name}") + except Exception as ex: + if "not found" in str(ex).lower(): + print(f"[OK] Table already removed: {table_name}") + else: + raise + + # ============================================================================ + # Summary + # ============================================================================ + print("\n" + "=" * 80) + print("Memo Walkthrough Complete!") + print("=" * 80) + print(f"\nAll assertions passed ({len(assertions_passed)}):") + for a in assertions_passed: + print(f" [OK] {a}") + print("=" * 80) + + +if __name__ == "__main__": + main() diff --git a/examples/advanced/perf_benchmark_live.py b/examples/advanced/perf_benchmark_live.py new file mode 100644 index 00000000..03f17f59 --- /dev/null +++ b/examples/advanced/perf_benchmark_live.py @@ -0,0 +1,255 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +""" +Live Dataverse performance benchmark for picklist label resolution. + +Creates a temporary table with a configurable number of picklist columns, +then measures wall-clock time and API call count for label resolution +at various scale points. + +Run from repo root: + $env:PYTHONPATH="src"; .conda/python.exe examples/advanced/perf_benchmark_live.py + +Prerequisites: + - pip install PowerPlatform-Dataverse-Client azure-identity + - DATAVERSE_URL environment variable set + - Interactive browser auth (or configure credentials below) + +WARNING: This script creates and deletes a temporary table on your +Dataverse environment. It cleans up after itself, but if interrupted +mid-run, you may need to manually delete the table (new_perfbench_*). +""" + +import os +import sys +import time +import uuid +from enum import IntEnum + +sys.path.insert(0, "src") + +from azure.identity import InteractiveBrowserCredential +from PowerPlatform.Dataverse.client import DataverseClient +from PowerPlatform.Dataverse.core.errors import MetadataError + + +# --------------------------------------------------------------------------- +# Configuration +# --------------------------------------------------------------------------- + +# Scale points: number of picklist columns to create on the test table. +# Dataverse has a max attribute limit (~400-500 custom columns per table), +# so we stay within that range. +SCALE_POINTS = [1, 10, 100, 250, 400] +OPTIONS_PER_PICKLIST = 4 +EXTRA_STRING_FIELDS = 3 # non-picklist string fields added at each scale +REPEAT_CALLS = 3 # warm cache repeat calls + + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + + +def make_picklist_enum(index: int, num_options: int) -> type: + """Dynamically create an IntEnum for a picklist column.""" + members = {f"Option_{j}": 100000000 + j for j in range(num_options)} + return IntEnum(f"Picklist{index}", members) + + +def backoff(op, *, delays=(0, 3, 10, 20)): + """Retry an operation with exponential backoff.""" + last_err = None + for delay in delays: + if delay: + time.sleep(delay) + try: + return op() + except Exception as e: + last_err = e + print(f" [WARN] Retry after {delay}s: {e}") + raise last_err + + +def create_test_table(client, table_name: str, num_picklists: int) -> dict: + """Create a test table with picklist + string columns.""" + schema = {} + + # Add picklist columns + for i in range(num_picklists): + col_name = f"new_Picklist{i}" + enum_cls = make_picklist_enum(i, OPTIONS_PER_PICKLIST) + schema[col_name] = enum_cls + + # Add plain string columns + for i in range(EXTRA_STRING_FIELDS): + schema[f"new_TextField{i}"] = "string" + + print(f" [INFO] Creating table {table_name} with {num_picklists} picklists + {EXTRA_STRING_FIELDS} text fields...") + info = client.tables.create(table_name, schema) + print(f" [OK] Table created: {info['entity_set_name']}") + return info + + +def build_test_record(num_picklists: int) -> dict: + """Build a record with label strings for all picklists + plain text.""" + record = {"new_name": f"perf-test-{uuid.uuid4().hex[:8]}"} + for i in range(num_picklists): + # Use the first option label + record[f"new_picklist{i}"] = "Option_0" + for i in range(EXTRA_STRING_FIELDS): + record[f"new_textfield{i}"] = f"plain text value {i}" + return record + + +def count_api_calls(client, table_name: str, record: dict) -> tuple: + """Measure a single _convert_labels_to_ints call. + + Returns (elapsed_ms, api_call_count). + We count calls by patching _request temporarily. + """ + odata = client._odata + original_request = odata._request + call_count = 0 + + def counting_request(*args, **kwargs): + nonlocal call_count + call_count += 1 + return original_request(*args, **kwargs) + + odata._request = counting_request + try: + t0 = time.perf_counter() + odata._convert_labels_to_ints(table_name, record) + elapsed = time.perf_counter() - t0 + finally: + odata._request = original_request + + return round(elapsed * 1000, 1), call_count + + +# --------------------------------------------------------------------------- +# Main benchmark +# --------------------------------------------------------------------------- + + +def main(): + url = sys.argv[1] if len(sys.argv) > 1 else os.environ.get("DATAVERSE_URL") + if not url: + url = input("Enter Dataverse URL (e.g. https://org.crm.dynamics.com): ").strip() + if not url: + print("[ERR] Dataverse URL required (pass as argument, set DATAVERSE_URL, or enter at prompt).") + sys.exit(1) + + print("=" * 78) + print("Picklist Label Resolution - Live Dataverse Benchmark") + print(f"Environment: {url}") + print(f"Scale points (picklist columns): {SCALE_POINTS}") + print(f"Options per picklist: {OPTIONS_PER_PICKLIST}") + print(f"Extra string fields: {EXTRA_STRING_FIELDS}") + print(f"Warm cache repeat calls: {REPEAT_CALLS}") + print("=" * 78) + + cred = InteractiveBrowserCredential() + client = DataverseClient(base_url=url, credential=cred) + + results = [] + run_id = uuid.uuid4().hex[:6] + + for scale_idx, num_picklists in enumerate(SCALE_POINTS): + table_name = f"new_PerfBench{run_id}_{num_picklists}p" + total_fields = num_picklists + EXTRA_STRING_FIELDS + print(f"\n--- Scale point {scale_idx + 1}/{len(SCALE_POINTS)}: " + f"{num_picklists} picklists, {total_fields} total fields ---") + + try: + # Create table + info = backoff(lambda: create_test_table(client, table_name, num_picklists)) + + # Wait for columns to be visible + print(f" [INFO] Waiting for columns to become visible...") + time.sleep(5) + + # Build test record + record = build_test_record(num_picklists) + + # Flush cache to ensure cold start + client._odata._picklist_label_cache.clear() + + # Cold cache measurement + print(f" [INFO] Cold cache measurement...") + cold_ms, cold_calls = count_api_calls(client, table_name, record) + print(f" [OK] Cold: {cold_calls} API calls, {cold_ms}ms") + + # Warm cache measurements + warm_times = [] + warm_calls_list = [] + for rep in range(REPEAT_CALLS): + ms, calls = count_api_calls(client, table_name, record) + warm_times.append(ms) + warm_calls_list.append(calls) + + avg_warm_ms = round(sum(warm_times) / len(warm_times), 1) + avg_warm_calls = round(sum(warm_calls_list) / len(warm_calls_list), 1) + print(f" [OK] Warm (avg {REPEAT_CALLS}x): {avg_warm_calls} API calls, {avg_warm_ms}ms") + + results.append({ + "picklists": num_picklists, + "total_fields": total_fields, + "cold_calls": cold_calls, + "cold_ms": cold_ms, + "warm_calls": avg_warm_calls, + "warm_ms": avg_warm_ms, + }) + + except Exception as e: + print(f" [ERR] Scale point failed: {e}") + results.append({ + "picklists": num_picklists, + "total_fields": total_fields, + "cold_calls": "ERR", + "cold_ms": "ERR", + "warm_calls": "ERR", + "warm_ms": "ERR", + }) + + finally: + # Cleanup: delete table + print(f" [INFO] Cleaning up table {table_name}...") + try: + backoff(lambda: client.tables.delete(table_name)) + print(f" [OK] Table deleted.") + except Exception as e: + print(f" [WARN] Cleanup failed: {e}") + + # Print results + print("\n" + "=" * 78) + print("RESULTS") + print("=" * 78) + header = ( + f"{'Picklists':>9} | {'Total':>5} | " + f"{'Cold Calls':>10} | {'Cold ms':>8} | " + f"{'Warm Calls':>10} | {'Warm ms':>8}" + ) + print(header) + print("-" * len(header)) + for r in results: + print( + f"{r['picklists']:>9} | {r['total_fields']:>5} | " + f"{str(r['cold_calls']):>10} | {str(r['cold_ms']):>8} | " + f"{str(r['warm_calls']):>10} | {str(r['warm_ms']):>8}" + ) + + print("\n" + "-" * 78) + print("Notes:") + print(" - Cold = first call with empty cache (metadata fetched from Dataverse)") + print(f" - Warm = average of {REPEAT_CALLS} repeat calls (cache populated)") + print(" - Times include real network latency to Dataverse") + + print("\n[OK] Live benchmark complete.") + client.close() + + +if __name__ == "__main__": + main() diff --git a/examples/advanced/perf_benchmark_mock.py b/examples/advanced/perf_benchmark_mock.py new file mode 100644 index 00000000..7eeba543 --- /dev/null +++ b/examples/advanced/perf_benchmark_mock.py @@ -0,0 +1,309 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +""" +Mock-based performance benchmark for picklist label resolution. + +Compares API call count and wall-clock time across different scale points +(number of string fields / picklists in a record). Uses mocked HTTP +responses with configurable simulated latency to isolate the algorithmic +difference between approaches. + +Run from repo root: + $env:PYTHONPATH="src"; .conda/python.exe examples/advanced/perf_benchmark_mock.py + +The script works with whatever picklist resolution approach is currently +checked out (Option B or Option C). Switch branches and re-run to compare. +""" + +import sys +import time +from unittest.mock import MagicMock, patch, PropertyMock + +sys.path.insert(0, "src") + +from PowerPlatform.Dataverse.data._odata import _ODataClient + + +# --------------------------------------------------------------------------- +# Configuration +# --------------------------------------------------------------------------- + +SCALE_POINTS = [1, 10, 100, 500, 1000] # number of picklist columns +EXTRA_STRING_FIELDS = 3 # non-picklist string fields (fixed, same as live) +OPTIONS_PER_PICKLIST = 4 # number of options per picklist attribute (same as live) +SIMULATED_LATENCY_MS = 150 # typical Dataverse metadata response time +REPEAT_CALLS = 3 # number of repeat calls to measure cache effect +CSV_OUTPUT = False # set True to emit CSV instead of table + + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + + +def _make_option(value: int, label: str) -> dict: + """Build a realistic OptionMetadata payload.""" + return { + "Value": value, + "Label": { + "LocalizedLabels": [ + {"Label": label, "LanguageCode": 1033} + ] + }, + } + + +def _build_bulk_response(num_picklists: int, options_per: int) -> dict: + """Build a PicklistAttributeMetadata bulk response for Option C.""" + items = [] + for i in range(num_picklists): + attr_name = f"new_picklist_{i}" + options = [ + _make_option(j, f"Label_{i}_{j}") + for j in range(options_per) + ] + items.append({ + "LogicalName": attr_name, + "OptionSet": {"Options": options}, + }) + return {"value": items} + + +def _build_type_check_response(attr_names: list, picklist_names: set) -> dict: + """Build a CRM.In type-check response for Option B.""" + items = [] + for name in attr_names: + if name in picklist_names: + items.append({ + "LogicalName": name, + "@odata.type": "#Microsoft.Dynamics.CRM.PicklistAttributeMetadata", + }) + return {"value": items} + + +def _build_single_optionset_response(attr_index: int, options_per: int) -> dict: + """Build a single attribute OptionSet response for Option B.""" + options = [ + _make_option(j, f"Label_{attr_index}_{j}") + for j in range(options_per) + ] + return { + "value": [{ + "LogicalName": f"new_picklist_{attr_index}", + "OptionSet": {"Options": options}, + }] + } + + +def _build_record(num_string_fields: int, num_picklists: int) -> dict: + """Build a test record with string values (labels for picklists, plain for others).""" + record = {} + for i in range(num_picklists): + # Use first label option for each picklist + record[f"new_picklist_{i}"] = f"Label_{i}_0" + for i in range(num_string_fields - num_picklists): + record[f"new_textfield_{i}"] = f"some text value {i}" + return record + + +def _create_client() -> _ODataClient: + """Create an _ODataClient with mocked auth.""" + mock_auth = MagicMock() + mock_token = MagicMock() + mock_token.access_token = "fake-token" + mock_auth._acquire_token.return_value = mock_token + + mock_config = MagicMock() + mock_config.http_retries = 0 + mock_config.http_backoff = 0 + mock_config.http_timeout = 30 + mock_config.language_code = 1033 + + client = _ODataClient( + auth=mock_auth, + base_url="https://mock.crm.dynamics.com", + config=mock_config, + ) + return client + + +# --------------------------------------------------------------------------- +# Benchmark runner +# --------------------------------------------------------------------------- + + +def run_benchmark(num_picklists: int) -> dict: + """Run a single scale point and return metrics.""" + num_plain = EXTRA_STRING_FIELDS + num_fields = num_picklists + num_plain + record = _build_record(num_fields, num_picklists) + latency_s = SIMULATED_LATENCY_MS / 1000.0 + + # Track API calls + api_call_count = 0 + + def counting_request(method, url, **kwargs): + """Mock _request that counts calls and adds simulated latency.""" + nonlocal api_call_count + api_call_count += 1 + time.sleep(latency_s) + + mock_resp = MagicMock() + mock_resp.status_code = 200 + + # Detect which kind of request this is by URL pattern + url_lower = url.lower() + + # Option C: PicklistAttributeMetadata bulk fetch + if "picklistattributemetadata" in url_lower: + mock_resp.json.return_value = _build_bulk_response(num_picklists, OPTIONS_PER_PICKLIST) + return mock_resp + + # Option B: CRM.In batch type check + if "crm.in" in url_lower or "microsoft.dynamics.crm.in" in url_lower: + picklist_names = {f"new_picklist_{i}" for i in range(num_picklists)} + all_names = list(record.keys()) + mock_resp.json.return_value = _build_type_check_response(all_names, picklist_names) + return mock_resp + + # Option B: individual optionset fetch (per-attribute) + if "optionset" in url_lower or "globaloptionsetdefinitions" in url_lower: + # Extract attr index from URL heuristically + for i in range(num_picklists): + if f"new_picklist_{i}" in url_lower: + mock_resp.json.return_value = _build_single_optionset_response(i, OPTIONS_PER_PICKLIST) + return mock_resp + mock_resp.json.return_value = {"value": []} + return mock_resp + + # Option B fallback: per-attribute type check (old approach) + if "attributetype" in url_lower or "attributes" in url_lower: + # Check if URL references a picklist attribute + for i in range(num_picklists): + if f"new_picklist_{i}" in url_lower: + mock_resp.json.return_value = { + "value": [{ + "LogicalName": f"new_picklist_{i}", + "@odata.type": "#Microsoft.Dynamics.CRM.PicklistAttributeMetadata", + "AttributeType": "Picklist", + }] + } + return mock_resp + # Plain string attribute + mock_resp.json.return_value = { + "value": [{ + "@odata.type": "#Microsoft.Dynamics.CRM.StringAttributeMetadata", + "AttributeType": "String", + }] + } + return mock_resp + + # Default fallback + mock_resp.json.return_value = {"value": []} + return mock_resp + + # --- Cold cache run --- + client = _create_client() + with patch.object(client, "_request", side_effect=counting_request): + api_call_count = 0 + t0 = time.perf_counter() + result = client._convert_labels_to_ints("new_perftable", record) + cold_time = time.perf_counter() - t0 + cold_calls = api_call_count + + # Verify resolution worked (at least some labels should be ints now) + resolved_count = sum(1 for v in result.values() if isinstance(v, int)) + + # --- Warm cache runs --- + warm_times = [] + warm_calls_list = [] + for _ in range(REPEAT_CALLS): + with patch.object(client, "_request", side_effect=counting_request): + api_call_count = 0 + t0 = time.perf_counter() + client._convert_labels_to_ints("new_perftable", record) + warm_times.append(time.perf_counter() - t0) + warm_calls_list.append(api_call_count) + + avg_warm_time = sum(warm_times) / len(warm_times) + avg_warm_calls = sum(warm_calls_list) / len(warm_calls_list) + + return { + "fields": num_fields, + "picklists": num_picklists, + "plain_strings": num_plain, + "cold_calls": cold_calls, + "cold_time_ms": round(cold_time * 1000, 1), + "warm_calls": round(avg_warm_calls, 1), + "warm_time_ms": round(avg_warm_time * 1000, 1), + "resolved": resolved_count, + } + + +def main(): + print("=" * 78) + print("Picklist Label Resolution - Mock Performance Benchmark") + print(f"Simulated latency: {SIMULATED_LATENCY_MS}ms per API call") + print(f"Extra string fields: {EXTRA_STRING_FIELDS}") + print(f"Options per picklist: {OPTIONS_PER_PICKLIST}") + print(f"Warm cache repeat calls: {REPEAT_CALLS}") + print("=" * 78) + + results = [] + for n in SCALE_POINTS: + print(f"\n[INFO] Running scale point: {n} picklists ...", end="", flush=True) + r = run_benchmark(n) + results.append(r) + print(f" done (cold={r['cold_calls']} calls, {r['cold_time_ms']}ms)") + + # Print results table + print("\n" + "=" * 78) + print("RESULTS") + print("=" * 78) + header = ( + f"{'Fields':>7} | {'Picklists':>9} | {'Plain':>5} | " + f"{'Cold Calls':>10} | {'Cold ms':>8} | " + f"{'Warm Calls':>10} | {'Warm ms':>8} | {'Resolved':>8}" + ) + print(header) + print("-" * len(header)) + for r in results: + print( + f"{r['fields']:>7} | {r['picklists']:>9} | {r['plain_strings']:>5} | " + f"{r['cold_calls']:>10} | {r['cold_time_ms']:>8} | " + f"{r['warm_calls']:>10} | {r['warm_time_ms']:>8} | {r['resolved']:>8}" + ) + + print("\n" + "-" * 78) + print("Notes:") + print(f" - Cold = first call (cache empty, metadata fetched)") + print(f" - Warm = average of {REPEAT_CALLS} repeat calls (cache populated)") + print(f" - Resolved = number of label strings converted to ints") + print(f" - Times include {SIMULATED_LATENCY_MS}ms simulated latency per API call") + + if CSV_OUTPUT: + print("\n--- CSV ---") + print("fields,picklists,plain,cold_calls,cold_ms,warm_calls,warm_ms,resolved") + for r in results: + print( + f"{r['fields']},{r['picklists']},{r['plain_strings']}," + f"{r['cold_calls']},{r['cold_time_ms']}," + f"{r['warm_calls']},{r['warm_time_ms']},{r['resolved']}" + ) + + # Summary + first = results[0] + last = results[-1] + print(f"\n[INFO] Scaling: {first['fields']} fields -> {first['cold_calls']} API calls; " + f"{last['fields']} fields -> {last['cold_calls']} API calls") + if last["cold_calls"] <= 2: + print("[INFO] Approach: constant API calls (Option C - bulk fetch)") + elif last["cold_calls"] > first["cold_calls"] * 5: + print("[INFO] Approach: API calls scale with field count (Option B or baseline)") + + print("\n[OK] Benchmark complete.") + + +if __name__ == "__main__": + main() diff --git a/examples/advanced/picklist_walkthrough.py b/examples/advanced/picklist_walkthrough.py new file mode 100644 index 00000000..fba8f8fa --- /dev/null +++ b/examples/advanced/picklist_walkthrough.py @@ -0,0 +1,440 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +""" +Walkthrough demonstrating picklist label-to-integer resolution. + +This example exercises all edge cases of the SDK's automatic picklist +label resolution, including: +- Single picklist field (create and update) +- Multiple picklist fields in one record +- Mixed picklist + non-picklist string fields +- Integer values passed through unchanged +- Unmatched labels left as strings (graceful fallback) +- Warm cache (second call skips metadata lookups) +- Case-insensitive label matching + +Prerequisites: +- pip install PowerPlatform-Dataverse-Client +- pip install azure-identity +""" + +import json +import sys +import time +from enum import IntEnum +from azure.identity import InteractiveBrowserCredential +from PowerPlatform.Dataverse.client import DataverseClient +from PowerPlatform.Dataverse.core.errors import MetadataError +import requests + + +# Simple logging helper +def log_call(description): + print(f"\n-> {description}") + + +# Two picklist enums to test multiple picklists in one table +class Priority(IntEnum): + LOW = 1 + MEDIUM = 2 + HIGH = 3 + + +class Status(IntEnum): + DRAFT = 100000000 + ACTIVE = 100000001 + CLOSED = 100000002 + + +def backoff(op, *, delays=(0, 2, 5, 10, 20, 20)): + last = None + total_delay = 0 + attempts = 0 + for d in delays: + if d: + time.sleep(d) + total_delay += d + attempts += 1 + try: + result = op() + if attempts > 1: + retry_count = attempts - 1 + print(f" [INFO] Backoff succeeded after {retry_count} retry(s); waited {total_delay}s total.") + return result + except Exception as ex: + last = ex + continue + if last: + if attempts: + retry_count = max(attempts - 1, 0) + print(f" [WARN] Backoff exhausted after {retry_count} retry(s); waited {total_delay}s total.") + raise last + + +def main(): + print("=" * 80) + print("Picklist Label Resolution Walkthrough") + print("=" * 80) + + # ============================================================================ + # 1. SETUP & AUTHENTICATION + # ============================================================================ + print("\n" + "=" * 80) + print("1. Setup & Authentication") + print("=" * 80) + + base_url = sys.argv[1] if len(sys.argv) > 1 else "" + if not base_url: + base_url = input("Enter Dataverse org URL (e.g. https://yourorg.crm.dynamics.com): ").strip() + if not base_url: + print("No URL entered; exiting.") + sys.exit(1) + + base_url = base_url.rstrip("/") + + log_call("InteractiveBrowserCredential()") + credential = InteractiveBrowserCredential() + + log_call(f"DataverseClient(base_url='{base_url}', credential=...)") + with DataverseClient(base_url=base_url, credential=credential) as client: + print(f"[OK] Connected to: {base_url}") + _run_walkthrough(client) + + +def _run_walkthrough(client): + table_name = "new_PicklistDemo" + + # ============================================================================ + # 2. TABLE CREATION WITH TWO PICKLISTS + # ============================================================================ + print("\n" + "=" * 80) + print("2. Table Creation (two picklist columns)") + print("=" * 80) + + log_call(f"client.tables.get('{table_name}')") + table_info = backoff(lambda: client.tables.get(table_name)) + + if table_info: + print(f"[OK] Table already exists: {table_name}") + else: + log_call(f"client.tables.create('{table_name}', columns={{...}})") + columns = { + "new_Title": "string", + "new_Description": "string", + "new_Priority": Priority, + "new_Status": Status, + "new_Count": "int", + } + table_info = backoff(lambda: client.tables.create(table_name, columns)) + print(f"[OK] Created table: {table_name}") + print(f" Columns: {', '.join(table_info.get('columns_created', []))}") + + # ============================================================================ + # 3. SINGLE PICKLIST LABEL (CREATE) + # ============================================================================ + print("\n" + "=" * 80) + print("3. Single Picklist Label (Create)") + print("=" * 80) + + log_call(f"client.records.create('{table_name}', {{'new_Priority': 'High'}})") + record1 = { + "new_Title": "Single picklist test", + "new_Priority": "High", + } + id1 = backoff(lambda: client.records.create(table_name, record1)) + retrieved1 = backoff(lambda: client.records.get(table_name, id1)) + print(f"[OK] Created with label 'High'") + print(f" new_priority = {retrieved1.get('new_priority')} (expected: 3)") + assert retrieved1.get("new_priority") == 3, "Expected 3 for 'High'" + + # Print the cached picklist metadata for visibility + odata = client._get_odata() + table_key = table_name.lower() + cache_entry = odata._picklist_label_cache.get(table_key, {}) + print(f"\n [DEBUG] Picklist cache for '{table_key}':") + picklists = cache_entry.get("picklists", {}) + print(f" Cached {len(picklists)} picklist attribute(s):") + for attr, mapping in picklists.items(): + print(f" {attr}: {json.dumps(mapping, indent=6)}") + + # ============================================================================ + # 4. SINGLE PICKLIST LABEL (UPDATE) + # ============================================================================ + print("\n" + "=" * 80) + print("4. Single Picklist Label (Update)") + print("=" * 80) + + log_call(f"client.records.update('{table_name}', id1, {{'new_Priority': 'Low'}})") + backoff(lambda: client.records.update(table_name, id1, {"new_Priority": "Low"})) + updated1 = backoff(lambda: client.records.get(table_name, id1)) + print(f"[OK] Updated with label 'Low'") + print(f" new_priority = {updated1.get('new_priority')} (expected: 1)") + assert updated1.get("new_priority") == 1, "Expected 1 for 'Low'" + + # ============================================================================ + # 5. MULTIPLE PICKLISTS IN ONE RECORD + # ============================================================================ + print("\n" + "=" * 80) + print("5. Multiple Picklists in One Record") + print("=" * 80) + + log_call(f"client.records.create('{table_name}', {{'new_Priority': 'Medium', 'new_Status': 'Active'}})") + record2 = { + "new_Title": "Two picklists test", + "new_Priority": "Medium", + "new_Status": "Active", + } + id2 = backoff(lambda: client.records.create(table_name, record2)) + retrieved2 = backoff(lambda: client.records.get(table_name, id2)) + print(f"[OK] Created with two picklist labels") + print(f" new_priority = {retrieved2.get('new_priority')} (expected: 2)") + print(f" new_status = {retrieved2.get('new_status')} (expected: 100000001)") + assert retrieved2.get("new_priority") == 2, "Expected 2 for 'Medium'" + assert retrieved2.get("new_status") == 100000001, "Expected 100000001 for 'Active'" + + # ============================================================================ + # 6. MIXED PICKLIST + NON-PICKLIST STRINGS + # ============================================================================ + print("\n" + "=" * 80) + print("6. Mixed Picklist + Non-Picklist Strings") + print("=" * 80) + + log_call(f"client.records.create('{table_name}', {{picklist + string fields}})") + record3 = { + "new_Title": "Mixed fields test", + "new_Description": "This is a plain string, not a picklist", + "new_Priority": "Low", + "new_Status": "Draft", + "new_Count": 42, + } + id3 = backoff(lambda: client.records.create(table_name, record3)) + retrieved3 = backoff(lambda: client.records.get(table_name, id3)) + print(f"[OK] Created with mixed fields") + print(f" new_priority = {retrieved3.get('new_priority')} (expected: 1)") + print(f" new_status = {retrieved3.get('new_status')} (expected: 100000000)") + print(f" new_description = '{retrieved3.get('new_description')}' (expected: unchanged string)") + print(f" new_count = {retrieved3.get('new_count')} (expected: 42)") + assert retrieved3.get("new_priority") == 1, "Expected 1 for 'Low'" + assert retrieved3.get("new_status") == 100000000, "Expected 100000000 for 'Draft'" + assert retrieved3.get("new_description") == "This is a plain string, not a picklist", "String should be unchanged" + assert retrieved3.get("new_count") == 42, "Integer should be unchanged" + + # ============================================================================ + # 7. INTEGER VALUES PASSED THROUGH + # ============================================================================ + print("\n" + "=" * 80) + print("7. Integer Values Passed Through") + print("=" * 80) + + log_call(f"client.records.create('{table_name}', {{'new_Priority': 3}})") + record4 = { + "new_Title": "Integer value test", + "new_Priority": 3, + "new_Status": 100000002, + } + id4 = backoff(lambda: client.records.create(table_name, record4)) + retrieved4 = backoff(lambda: client.records.get(table_name, id4)) + print(f"[OK] Created with integer values (no label resolution needed)") + print(f" new_priority = {retrieved4.get('new_priority')} (expected: 3)") + print(f" new_status = {retrieved4.get('new_status')} (expected: 100000002)") + assert retrieved4.get("new_priority") == 3 + assert retrieved4.get("new_status") == 100000002 + + # ============================================================================ + # 8. UNMATCHED LABEL LEFT AS STRING + # ============================================================================ + print("\n" + "=" * 80) + print("8. Unmatched Label (Graceful Fallback)") + print("=" * 80) + + log_call(f"client.records.create('{table_name}', {{'new_Title': 'UnknownLabel'}})") + # new_Title is a string column, not a picklist -- SDK should pass it through + # even though it looks like it could be a label + record5 = { + "new_Title": "UnknownLabel", + "new_Count": 7, + } + id5 = backoff(lambda: client.records.create(table_name, record5)) + retrieved5 = backoff(lambda: client.records.get(table_name, id5)) + print(f"[OK] String value for non-picklist field passed through") + print(f" new_title = '{retrieved5.get('new_title')}' (expected: 'UnknownLabel')") + assert retrieved5.get("new_title") == "UnknownLabel", "Non-picklist string should be unchanged" + + # ============================================================================ + # 9. CASE-INSENSITIVE LABEL MATCHING + # ============================================================================ + print("\n" + "=" * 80) + print("9. Case-Insensitive Label Matching") + print("=" * 80) + + log_call(f"client.records.create('{table_name}', {{'new_Priority': 'HIGH'}})") + record6 = { + "new_Title": "Case test - uppercase", + "new_Priority": "HIGH", + } + id6 = backoff(lambda: client.records.create(table_name, record6)) + retrieved6 = backoff(lambda: client.records.get(table_name, id6)) + print(f"[OK] 'HIGH' (uppercase) resolved correctly") + print(f" new_priority = {retrieved6.get('new_priority')} (expected: 3)") + assert retrieved6.get("new_priority") == 3, "Case-insensitive match failed" + + log_call(f"client.records.create('{table_name}', {{'new_Priority': 'medium'}})") + record7 = { + "new_Title": "Case test - lowercase", + "new_Priority": "medium", + } + id7 = backoff(lambda: client.records.create(table_name, record7)) + retrieved7 = backoff(lambda: client.records.get(table_name, id7)) + print(f"[OK] 'medium' (lowercase) resolved correctly") + print(f" new_priority = {retrieved7.get('new_priority')} (expected: 2)") + assert retrieved7.get("new_priority") == 2, "Case-insensitive match failed" + + # ============================================================================ + # 10. WARM CACHE (SECOND CALL SKIPS METADATA) + # ============================================================================ + print("\n" + "=" * 80) + print("10. Warm Cache (Second Call)") + print("=" * 80) + + log_call(f"client.records.create('{table_name}', {{...}}) -- should use cached metadata") + record8 = { + "new_Title": "Warm cache test", + "new_Priority": "Low", + "new_Status": "Closed", + "new_Description": "Cache should be warm from previous calls", + } + id8 = backoff(lambda: client.records.create(table_name, record8)) + retrieved8 = backoff(lambda: client.records.get(table_name, id8)) + print(f"[OK] Created using warm cache (no extra metadata calls)") + print(f" new_priority = {retrieved8.get('new_priority')} (expected: 1)") + print(f" new_status = {retrieved8.get('new_status')} (expected: 100000002)") + assert retrieved8.get("new_priority") == 1 + assert retrieved8.get("new_status") == 100000002 + + # ============================================================================ + # 11. UPDATE WITH MULTIPLE PICKLIST LABELS + # ============================================================================ + print("\n" + "=" * 80) + print("11. Update with Multiple Picklist Labels") + print("=" * 80) + + log_call(f"client.records.update('{table_name}', id8, {{'new_Priority': 'High', 'new_Status': 'Active'}})") + backoff( + lambda: client.records.update( + table_name, + id8, + {"new_Priority": "High", "new_Status": "Active"}, + ) + ) + updated8 = backoff(lambda: client.records.get(table_name, id8)) + print(f"[OK] Updated with two picklist labels") + print(f" new_priority = {updated8.get('new_priority')} (expected: 3)") + print(f" new_status = {updated8.get('new_status')} (expected: 100000001)") + assert updated8.get("new_priority") == 3 + assert updated8.get("new_status") == 100000001 + + # ============================================================================ + # 12. MIXED INT AND LABEL IN SAME RECORD + # ============================================================================ + print("\n" + "=" * 80) + print("12. Mixed Integer + Label in Same Record") + print("=" * 80) + + log_call(f"client.records.create('{table_name}', {{'new_Priority': 2, 'new_Status': 'Closed'}})") + record9 = { + "new_Title": "Mixed int+label test", + "new_Priority": 2, # already an int + "new_Status": "Closed", # label to resolve + } + id9 = backoff(lambda: client.records.create(table_name, record9)) + retrieved9 = backoff(lambda: client.records.get(table_name, id9)) + print(f"[OK] Created with int for Priority, label for Status") + print(f" new_priority = {retrieved9.get('new_priority')} (expected: 2, passed through)") + print(f" new_status = {retrieved9.get('new_status')} (expected: 100000002, resolved from 'Closed')") + assert retrieved9.get("new_priority") == 2, "Int value should pass through unchanged" + assert retrieved9.get("new_status") == 100000002, "Expected 100000002 for 'Closed'" + + # ============================================================================ + # 13. FULL REALISTIC UPDATE (picklists + strings + non-strings) + # ============================================================================ + print("\n" + "=" * 80) + print("13. Full Realistic Update (All Field Types)") + print("=" * 80) + + log_call(f"client.records.update('{table_name}', id9, {{picklists + strings + int}})") + backoff( + lambda: client.records.update( + table_name, + id9, + { + "new_Priority": "High", + "new_Status": "Active", + "new_Description": "Updated description text", + "new_Count": 99, + }, + ) + ) + updated9 = backoff(lambda: client.records.get(table_name, id9)) + print(f"[OK] Updated with all field types in one call") + print(f" new_priority = {updated9.get('new_priority')} (expected: 3)") + print(f" new_status = {updated9.get('new_status')} (expected: 100000001)") + print(f" new_description = '{updated9.get('new_description')}' (expected: unchanged string)") + print(f" new_count = {updated9.get('new_count')} (expected: 99)") + assert updated9.get("new_priority") == 3 + assert updated9.get("new_status") == 100000001 + assert updated9.get("new_description") == "Updated description text" + assert updated9.get("new_count") == 99 + + # ============================================================================ + # 14. CLEANUP + # ============================================================================ + print("\n" + "=" * 80) + print("14. Cleanup") + print("=" * 80) + + all_ids = [id1, id2, id3, id4, id5, id6, id7, id8, id9] + + log_call(f"client.records.delete('{table_name}', [{len(all_ids)} IDs])") + backoff(lambda: client.records.delete(table_name, all_ids)) + print(f"[OK] Deleted {len(all_ids)} records") + + log_call(f"client.tables.delete('{table_name}')") + try: + backoff(lambda: client.tables.delete(table_name)) + print(f"[OK] Deleted table: {table_name}") + except MetadataError as ex: + if "not found" in str(ex).lower(): + print(f"[OK] Table already removed: {table_name}") + else: + raise + except Exception as ex: + code = getattr(getattr(ex, "response", None), "status_code", None) + if isinstance(ex, requests.exceptions.HTTPError) and code == 404: + print(f"[OK] Table removed: {table_name}") + else: + raise + + # ============================================================================ + # SUMMARY + # ============================================================================ + print("\n" + "=" * 80) + print("Picklist Walkthrough Complete!") + print("=" * 80) + print("\nAll assertions passed:") + print(" [OK] Single picklist label (create)") + print(" [OK] Single picklist label (update)") + print(" [OK] Multiple picklists in one record") + print(" [OK] Mixed picklist + non-picklist strings") + print(" [OK] Integer values passed through unchanged") + print(" [OK] Unmatched label for non-picklist (graceful fallback)") + print(" [OK] Case-insensitive label matching (uppercase + lowercase)") + print(" [OK] Warm cache (second call uses cached metadata)") + print(" [OK] Update with multiple picklist labels") + print(" [OK] Mixed integer + label in same record") + print(" [OK] Full realistic update (picklists + strings + non-strings)") + print("=" * 80) + + +if __name__ == "__main__": + main() diff --git a/examples/advanced/test_querybuilder_live.py b/examples/advanced/test_querybuilder_live.py new file mode 100644 index 00000000..bcb1437a --- /dev/null +++ b/examples/advanced/test_querybuilder_live.py @@ -0,0 +1,955 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +""" +Self-contained QueryBuilder live integration test. + +Creates a test table with known data, runs every QueryBuilder method and filter +expression against it, then tears everything down. + +Run: + $env:PYTHONPATH = "src"; python examples/advanced/test_querybuilder_live.py +""" + +import sys +import time +import traceback +from enum import IntEnum + +from azure.identity import InteractiveBrowserCredential +from PowerPlatform.Dataverse.client import DataverseClient +from PowerPlatform.Dataverse.models.filters import ( + eq, + ne, + gt, + ge, + lt, + le, + contains, + startswith, + endswith, + between, + is_null, + is_not_null, + filter_in, + not_in, + not_between, + raw, +) +import requests + + +# --------------------------------------------------------------------------- +# Enum for picklist column +# --------------------------------------------------------------------------- + +class Priority(IntEnum): + LOW = 1 + MEDIUM = 2 + HIGH = 3 + + +# --------------------------------------------------------------------------- +# Constants +# --------------------------------------------------------------------------- + +TABLE = "new_QBLiveTest" +PASSED = 0 +FAILED = 0 +SKIPPED = 0 +ERRORS = [] + + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + +def backoff(op, *, delays=(0, 2, 5, 10, 20, 20)): + """Retry an operation with exponential backoff.""" + last = None + total_delay = 0 + attempts = 0 + for d in delays: + if d: + time.sleep(d) + total_delay += d + attempts += 1 + try: + result = op() + if attempts > 1: + print(f" [INFO] Backoff succeeded after {attempts - 1} retry(s); waited {total_delay}s.") + return result + except Exception as ex: + last = ex + continue + if last: + if attempts: + print(f" [WARN] Backoff exhausted after {max(attempts - 1, 0)} retry(s); waited {total_delay}s.") + raise last + + +def test(name, fn, client): + """Run a single test and report pass/fail.""" + global PASSED, FAILED, SKIPPED + try: + fn(client) + PASSED += 1 + print(f" [PASS] {name}") + except Exception as ex: + msg = str(ex) + if "query node In is not supported" in msg: + SKIPPED += 1 + print(f" [SKIP] {name}: CRM.In not supported by this environment") + else: + FAILED += 1 + ERRORS.append((name, ex)) + print(f" [FAIL] {name}: {ex}") + traceback.print_exc() + + +# --------------------------------------------------------------------------- +# SETUP: create table + seed data +# --------------------------------------------------------------------------- + +def setup(client): + """Create the test table and seed known data. Returns dict of created IDs.""" + print("\n--- SETUP ---") + + # Delete table if it already exists (clean slate) + try: + existing = client.tables.get(TABLE) + if existing: + print(f" Table '{TABLE}' already exists — deleting first") + backoff(lambda: client.tables.delete(TABLE)) + print(f" Deleted old table") + time.sleep(5) + except Exception: + pass # table doesn't exist, that's fine + + columns = { + "new_Title": "string", + "new_Quantity": "int", + "new_Amount": "decimal", + "new_Completed": "bool", + "new_Priority": Priority, + } + backoff(lambda: client.tables.create(TABLE, columns)) + print(f" Created table: {TABLE}") + + # --- Core records (4 rows with distinct characteristics) --- + core = [ + { + "new_Title": "Complete project documentation", + "new_Quantity": 100, + "new_Amount": 1250.50, + "new_Completed": False, + "new_Priority": Priority.MEDIUM, + }, + { + "new_Title": "Review code changes", + "new_Quantity": 10, + "new_Amount": 500.00, + "new_Completed": True, + "new_Priority": Priority.HIGH, + }, + { + "new_Title": "Update test cases", + "new_Quantity": 8, + "new_Amount": 750.25, + "new_Completed": True, + "new_Priority": Priority.LOW, + }, + { + "new_Title": "Deploy to staging", + "new_Quantity": 3, + "new_Amount": 2000.00, + "new_Completed": False, + "new_Priority": Priority.HIGH, + }, + ] + core_ids = backoff(lambda: client.records.create(TABLE, core)) + print(f" Created {len(core_ids)} core records") + + # --- Paging records (20 rows, Qty 1-20) --- + paging = [ + { + "new_Title": f"Paging test item {i}", + "new_Quantity": i, + "new_Amount": i * 10.0, + "new_Completed": False, + "new_Priority": Priority.LOW, + } + for i in range(1, 21) + ] + paging_ids = backoff(lambda: client.records.create(TABLE, paging)) + print(f" Created {len(paging_ids)} paging records") + + return {"core_ids": core_ids, "paging_ids": paging_ids} + + +# --------------------------------------------------------------------------- +# TEARDOWN: delete all records + table +# --------------------------------------------------------------------------- + +def teardown(client, ids_dict): + """Delete created records and the test table.""" + print("\n--- TEARDOWN ---") + + all_ids = ids_dict.get("core_ids", []) + ids_dict.get("paging_ids", []) + + # Bulk delete records + if all_ids: + try: + job_id = backoff(lambda: client.records.delete(TABLE, all_ids)) + print(f" Bulk delete job: {job_id} ({len(all_ids)} records)") + time.sleep(5) # give bulk delete a moment + except Exception as ex: + print(f" [WARN] Bulk delete failed: {ex}") + + # Delete table + try: + backoff(lambda: client.tables.delete(TABLE)) + print(f" Deleted table: {TABLE}") + except Exception as ex: + print(f" [WARN] Could not delete table: {ex}") + + +# --------------------------------------------------------------------------- +# FLUENT FILTER METHOD TESTS +# --------------------------------------------------------------------------- + +def test_filter_eq(client): + """filter_eq: Completed == False.""" + recs = list( + client.query.builder(TABLE) + .select("new_title", "new_completed") + .filter_eq("new_Completed", False) + .top(25) + .execute() + ) + assert len(recs) > 0, "Expected at least 1 incomplete record" + for r in recs: + assert r["new_completed"] is False, f"Expected False, got {r['new_completed']}" + + +def test_filter_ne(client): + """filter_ne: Priority != LOW.""" + recs = list( + client.query.builder(TABLE) + .select("new_title", "new_priority") + .filter_ne("new_Priority", Priority.LOW) + .top(25) + .execute() + ) + assert len(recs) > 0, "Expected at least 1 non-LOW record" + for r in recs: + assert r["new_priority"] != Priority.LOW, "Got LOW priority unexpectedly" + + +def test_filter_gt(client): + """filter_gt: Quantity > 10.""" + recs = list( + client.query.builder(TABLE) + .select("new_title", "new_quantity") + .filter_gt("new_Quantity", 10) + .execute() + ) + assert len(recs) >= 1, f"Expected >= 1 record with Qty > 10, got {len(recs)}" + for r in recs: + assert r["new_quantity"] > 10, f"Expected > 10, got {r['new_quantity']}" + + +def test_filter_ge(client): + """filter_ge: Quantity >= 10.""" + recs = list( + client.query.builder(TABLE) + .select("new_title", "new_quantity") + .filter_ge("new_Quantity", 10) + .execute() + ) + assert len(recs) >= 2, f"Expected >= 2 records with Qty >= 10, got {len(recs)}" + for r in recs: + assert r["new_quantity"] >= 10, f"Expected >= 10, got {r['new_quantity']}" + + +def test_filter_lt(client): + """filter_lt: Quantity < 5.""" + recs = list( + client.query.builder(TABLE) + .select("new_title", "new_quantity") + .filter_lt("new_Quantity", 5) + .execute() + ) + assert len(recs) >= 1, f"Expected >= 1 record with Qty < 5, got {len(recs)}" + for r in recs: + assert r["new_quantity"] < 5, f"Expected < 5, got {r['new_quantity']}" + + +def test_filter_le(client): + """filter_le: Quantity <= 3.""" + recs = list( + client.query.builder(TABLE) + .select("new_title", "new_quantity") + .filter_le("new_Quantity", 3) + .execute() + ) + assert len(recs) >= 1, f"Expected >= 1 record with Qty <= 3, got {len(recs)}" + for r in recs: + assert r["new_quantity"] <= 3, f"Expected <= 3, got {r['new_quantity']}" + + +def test_filter_contains(client): + """filter_contains: title contains 'Paging'.""" + recs = list( + client.query.builder(TABLE) + .select("new_title") + .filter_contains("new_Title", "Paging") + .execute() + ) + assert len(recs) >= 1, "Expected records containing 'Paging'" + for r in recs: + assert "paging" in r["new_title"].lower(), f"Expected 'Paging' in '{r['new_title']}'" + + +def test_filter_startswith(client): + """filter_startswith: title starts with 'Paging'.""" + recs = list( + client.query.builder(TABLE) + .select("new_title") + .filter_startswith("new_Title", "Paging") + .execute() + ) + assert len(recs) >= 1, "Expected records starting with 'Paging'" + for r in recs: + assert r["new_title"].lower().startswith("paging"), f"'{r['new_title']}' doesn't start with 'Paging'" + + +def test_filter_endswith(client): + """filter_endswith: title ends with 'documentation'.""" + recs = list( + client.query.builder(TABLE) + .select("new_title") + .filter_endswith("new_Title", "documentation") + .execute() + ) + assert len(recs) >= 1, "Expected at least 1 record ending with 'documentation'" + + +def test_filter_null(client): + """filter_null: new_Amount is null (expect 0 — all have amounts).""" + recs = list( + client.query.builder(TABLE) + .select("new_title", "new_amount") + .filter_null("new_Amount") + .execute() + ) + assert isinstance(recs, list), "Expected a list result" + + +def test_filter_not_null(client): + """filter_not_null: new_Amount is not null → all records.""" + recs = list( + client.query.builder(TABLE) + .select("new_title", "new_amount") + .filter_not_null("new_Amount") + .execute() + ) + assert len(recs) >= 20, f"Expected >= 20 records with Amount set, got {len(recs)}" + + +def test_filter_between_decimal(client): + """filter_between: Amount between 500 and 1500.""" + recs = list( + client.query.builder(TABLE) + .select("new_title", "new_amount") + .filter_between("new_Amount", 500, 1500) + .execute() + ) + assert len(recs) >= 1, "Expected >= 1 record with Amount in [500, 1500]" + for r in recs: + assert 500 <= r["new_amount"] <= 1500, f"Amount {r['new_amount']} not in [500, 1500]" + + +def test_filter_between_int(client): + """filter_between: Quantity between 5 and 15.""" + recs = list( + client.query.builder(TABLE) + .select("new_title", "new_quantity") + .filter_between("new_Quantity", 5, 15) + .execute() + ) + assert len(recs) >= 1, "Expected >= 1 record with Quantity in [5, 15]" + for r in recs: + assert 5 <= r["new_quantity"] <= 15, f"Quantity {r['new_quantity']} not in [5, 15]" + + +def test_filter_raw(client): + """filter_raw: raw OData filter string.""" + recs = list( + client.query.builder(TABLE) + .select("new_title", "new_quantity") + .filter_raw("new_quantity ge 10 and new_quantity le 20") + .execute() + ) + assert len(recs) >= 1, "Expected records from raw filter" + for r in recs: + assert 10 <= r["new_quantity"] <= 20, f"Quantity {r['new_quantity']} not in [10, 20]" + + +def test_filter_in_ints(client): + """filter_in: Priority in [HIGH, LOW] using Microsoft.Dynamics.CRM.In.""" + recs = list( + client.query.builder(TABLE) + .select("new_title", "new_priority") + .filter_in("new_Priority", [Priority.HIGH, Priority.LOW]) + .execute() + ) + assert len(recs) >= 2, f"Expected >= 2 records with HIGH or LOW, got {len(recs)}" + for r in recs: + assert r["new_priority"] in (Priority.HIGH, Priority.LOW), ( + f"Expected HIGH or LOW, got {r['new_priority']}" + ) + + +def test_filter_in_strings(client): + """filter_in: title in known values using Microsoft.Dynamics.CRM.In.""" + recs = list( + client.query.builder(TABLE) + .select("new_title") + .filter_in("new_Title", ["Review code changes", "Deploy to staging"]) + .execute() + ) + assert len(recs) == 2, f"Expected 2 records, got {len(recs)}" + titles = {r["new_title"] for r in recs} + assert "Review code changes" in titles + assert "Deploy to staging" in titles + + +def test_filter_in_combined(client): + """filter_in combined with filter_eq via AND.""" + recs = list( + client.query.builder(TABLE) + .select("new_title", "new_priority", "new_completed") + .filter_eq("new_Completed", False) + .filter_in("new_Priority", [Priority.HIGH, Priority.MEDIUM]) + .execute() + ) + assert len(recs) >= 1, "Expected at least 1 record" + for r in recs: + assert r["new_completed"] is False + assert r["new_priority"] in (Priority.HIGH, Priority.MEDIUM) + + +def test_filter_not_in_ints(client): + """filter_not_in: Priority not in [LOW] → HIGH and MEDIUM only.""" + recs = list( + client.query.builder(TABLE) + .select("new_title", "new_priority") + .filter_not_in("new_Priority", [Priority.LOW]) + .top(25) + .execute() + ) + assert len(recs) >= 1, "Expected at least 1 non-LOW record" + for r in recs: + assert r["new_priority"] != Priority.LOW, ( + f"Expected not LOW, got {r['new_priority']}" + ) + + +def test_filter_not_between(client): + """filter_not_between: Quantity not in [5, 15].""" + recs = list( + client.query.builder(TABLE) + .select("new_title", "new_quantity") + .filter_not_between("new_Quantity", 5, 15) + .execute() + ) + assert len(recs) >= 1, "Expected records outside [5, 15]" + for r in recs: + assert r["new_quantity"] < 5 or r["new_quantity"] > 15, ( + f"Quantity {r['new_quantity']} is in [5, 15]" + ) + + +def test_where_not_in(client): + """where() with not_in expression.""" + recs = list( + client.query.builder(TABLE) + .select("new_title", "new_priority") + .where(not_in("new_Priority", [Priority.LOW])) + .top(25) + .execute() + ) + assert len(recs) >= 1, "Expected non-LOW records" + for r in recs: + assert r["new_priority"] != Priority.LOW + + +def test_where_not_between(client): + """where() with not_between expression.""" + recs = list( + client.query.builder(TABLE) + .select("new_title", "new_quantity") + .where(not_between("new_Quantity", 5, 15)) + .execute() + ) + assert len(recs) >= 1, "Expected records outside [5, 15]" + for r in recs: + assert r["new_quantity"] < 5 or r["new_quantity"] > 15 + + +# --------------------------------------------------------------------------- +# WHERE() WITH COMPOSABLE EXPRESSION TREES +# --------------------------------------------------------------------------- + +def test_where_eq(client): + """where() with eq expression.""" + recs = list( + client.query.builder(TABLE) + .select("new_title", "new_completed") + .where(eq("new_Completed", True)) + .execute() + ) + assert len(recs) >= 1, "Expected completed records" + for r in recs: + assert r["new_completed"] is True + + +def test_where_and(client): + """where() with & (AND) operator.""" + recs = list( + client.query.builder(TABLE) + .select("new_title", "new_quantity", "new_completed") + .where(eq("new_Completed", False) & gt("new_Quantity", 5)) + .execute() + ) + assert len(recs) >= 1, "Expected incomplete records with Qty > 5" + for r in recs: + assert r["new_completed"] is False and r["new_quantity"] > 5 + + +def test_where_or(client): + """where() with | (OR) operator.""" + recs = list( + client.query.builder(TABLE) + .select("new_title", "new_quantity") + .where(lt("new_Quantity", 3) | gt("new_Quantity", 18)) + .execute() + ) + assert len(recs) >= 1, "Expected records with Qty < 3 or Qty > 18" + for r in recs: + assert r["new_quantity"] < 3 or r["new_quantity"] > 18 + + +def test_where_not(client): + """where() with ~ (NOT) operator.""" + recs = list( + client.query.builder(TABLE) + .select("new_title", "new_completed") + .where(~eq("new_Completed", True)) + .execute() + ) + assert len(recs) >= 1, "Expected non-completed records" + for r in recs: + assert r["new_completed"] is False + + +def test_where_nested_and_or(client): + """where() with nested (A | B) & C.""" + recs = list( + client.query.builder(TABLE) + .select("new_title", "new_priority", "new_quantity") + .where( + (eq("new_Priority", Priority.HIGH) | eq("new_Priority", Priority.MEDIUM)) + & gt("new_Quantity", 2) + ) + .execute() + ) + assert len(recs) >= 1, "Expected HIGH/MEDIUM priority records with Qty > 2" + for r in recs: + assert r["new_priority"] in (Priority.HIGH, Priority.MEDIUM) + assert r["new_quantity"] > 2 + + +def test_where_contains(client): + """where() with contains expression.""" + recs = list( + client.query.builder(TABLE) + .select("new_title") + .where(contains("new_Title", "test")) + .execute() + ) + assert len(recs) >= 1, "Expected records containing 'test'" + for r in recs: + assert "test" in r["new_title"].lower() + + +def test_where_startswith(client): + """where() with startswith expression.""" + recs = list( + client.query.builder(TABLE) + .select("new_title") + .where(startswith("new_Title", "Deploy")) + .execute() + ) + assert len(recs) >= 1, "Expected records starting with 'Deploy'" + + +def test_where_endswith(client): + """where() with endswith expression.""" + recs = list( + client.query.builder(TABLE) + .select("new_title") + .where(endswith("new_Title", "staging")) + .execute() + ) + assert len(recs) >= 1, "Expected records ending with 'staging'" + + +def test_where_between(client): + """where() with between expression.""" + recs = list( + client.query.builder(TABLE) + .select("new_title", "new_amount") + .where(between("new_Amount", 100, 800)) + .execute() + ) + assert len(recs) >= 1, "Expected records with Amount in [100, 800]" + for r in recs: + assert 100 <= r["new_amount"] <= 800 + + +def test_where_is_null(client): + """where() with is_null (expect 0 — all have amounts).""" + recs = list( + client.query.builder(TABLE) + .select("new_title") + .where(is_null("new_Amount")) + .execute() + ) + assert isinstance(recs, list) + + +def test_where_is_not_null(client): + """where() with is_not_null.""" + recs = list( + client.query.builder(TABLE) + .select("new_title") + .where(is_not_null("new_Amount")) + .execute() + ) + assert len(recs) >= 20 + + +def test_where_raw(client): + """where() with raw expression.""" + recs = list( + client.query.builder(TABLE) + .select("new_title", "new_quantity") + .where(raw("new_quantity eq 100")) + .execute() + ) + assert len(recs) >= 1, "Expected record with Qty=100" + assert recs[0]["new_quantity"] == 100 + + +def test_where_filter_in(client): + """where() with filter_in expression composed with &.""" + recs = list( + client.query.builder(TABLE) + .select("new_title", "new_priority", "new_quantity") + .where(filter_in("new_Priority", [Priority.HIGH, Priority.MEDIUM]) & gt("new_Quantity", 5)) + .execute() + ) + assert len(recs) >= 1, "Expected HIGH/MEDIUM records with Qty > 5" + for r in recs: + assert r["new_priority"] in (Priority.HIGH, Priority.MEDIUM) + assert r["new_quantity"] > 5 + + +# --------------------------------------------------------------------------- +# COMBINED FLUENT + WHERE +# --------------------------------------------------------------------------- + +def test_combined_fluent_and_where(client): + """filter_eq + where(between).""" + recs = list( + client.query.builder(TABLE) + .select("new_title", "new_quantity", "new_completed") + .filter_eq("new_Completed", False) + .where(between("new_Quantity", 5, 15)) + .execute() + ) + assert len(recs) >= 1 + for r in recs: + assert r["new_completed"] is False + assert 5 <= r["new_quantity"] <= 15 + + +def test_combined_multiple_fluent(client): + """Chaining filter_ge + filter_le.""" + recs = list( + client.query.builder(TABLE) + .select("new_title", "new_quantity") + .filter_ge("new_Quantity", 5) + .filter_le("new_Quantity", 15) + .execute() + ) + assert len(recs) >= 1 + for r in recs: + assert 5 <= r["new_quantity"] <= 15 + + +# --------------------------------------------------------------------------- +# SELECT, ORDER_BY, TOP, PAGE_SIZE +# --------------------------------------------------------------------------- + +def test_select_columns(client): + """select() limits returned columns.""" + recs = list( + client.query.builder(TABLE) + .select("new_title") + .top(3) + .execute() + ) + assert len(recs) > 0 + assert "new_title" in recs[0] + + +def test_order_by_asc(client): + """order_by ascending.""" + recs = list( + client.query.builder(TABLE) + .select("new_title", "new_quantity") + .order_by("new_Quantity") + .top(5) + .execute() + ) + assert len(recs) == 5 + quantities = [r["new_quantity"] for r in recs] + assert quantities == sorted(quantities), f"Not ascending: {quantities}" + + +def test_order_by_desc(client): + """order_by descending.""" + recs = list( + client.query.builder(TABLE) + .select("new_title", "new_quantity") + .order_by("new_Quantity", descending=True) + .top(5) + .execute() + ) + assert len(recs) == 5 + quantities = [r["new_quantity"] for r in recs] + assert quantities == sorted(quantities, reverse=True), f"Not descending: {quantities}" + + +def test_top(client): + """top() limits result count.""" + recs = list( + client.query.builder(TABLE) + .select("new_title") + .top(3) + .execute() + ) + assert len(recs) == 3, f"Expected 3 records, got {len(recs)}" + + +def test_page_size_flat(client): + """page_size with flat iteration.""" + recs = list( + client.query.builder(TABLE) + .select("new_title") + .page_size(5) + .execute() + ) + assert len(recs) >= 20, f"Expected >= 20 records, got {len(recs)}" + + +def test_page_size_by_page(client): + """page_size with by_page=True.""" + pages = list( + client.query.builder(TABLE) + .select("new_title") + .page_size(5) + .execute(by_page=True) + ) + assert len(pages) >= 4, f"Expected >= 4 pages (24 records / 5), got {len(pages)}" + for i, p in enumerate(pages[:-1]): + assert len(p) == 5, f"Page {i + 1} has {len(p)} records, expected 5" + + +def test_page_size_by_page_with_filter(client): + """by_page=True with filter.""" + pages = list( + client.query.builder(TABLE) + .select("new_title", "new_quantity") + .filter_between("new_Quantity", 1, 10) + .page_size(3) + .execute(by_page=True) + ) + total = sum(len(p) for p in pages) + assert total >= 1, "Expected at least 1 record in range" + for page in pages: + for r in page: + assert 1 <= r["new_quantity"] <= 10 + + +# --------------------------------------------------------------------------- +# EDGE CASES +# --------------------------------------------------------------------------- + +def test_no_results(client): + """Query returning 0 results → empty list, not error.""" + recs = list( + client.query.builder(TABLE) + .select("new_title") + .filter_eq("new_Quantity", 999999) + .execute() + ) + assert len(recs) == 0, f"Expected 0 results, got {len(recs)}" + + +def test_no_filters(client): + """Query with no filters → all records.""" + recs = list( + client.query.builder(TABLE) + .select("new_title") + .execute() + ) + assert len(recs) >= 24, f"Expected >= 24 records (4 core + 20 paging), got {len(recs)}" + + +def test_chaining_order(client): + """select before/after filter should produce same results.""" + recs1 = list( + client.query.builder(TABLE) + .select("new_title", "new_quantity") + .filter_eq("new_Completed", False) + .order_by("new_Quantity") + .top(5) + .execute() + ) + recs2 = list( + client.query.builder(TABLE) + .filter_eq("new_Completed", False) + .order_by("new_Quantity") + .select("new_title", "new_quantity") + .top(5) + .execute() + ) + titles1 = [r["new_title"] for r in recs1] + titles2 = [r["new_title"] for r in recs2] + assert titles1 == titles2, f"Order mismatch: {titles1} vs {titles2}" + + +# --------------------------------------------------------------------------- +# TEST REGISTRY +# --------------------------------------------------------------------------- + +ALL_TESTS = [ + # Fluent filter methods + ("filter_eq", test_filter_eq), + ("filter_ne", test_filter_ne), + ("filter_gt", test_filter_gt), + ("filter_ge", test_filter_ge), + ("filter_lt", test_filter_lt), + ("filter_le", test_filter_le), + ("filter_contains", test_filter_contains), + ("filter_startswith", test_filter_startswith), + ("filter_endswith", test_filter_endswith), + ("filter_null", test_filter_null), + ("filter_not_null", test_filter_not_null), + ("filter_between (decimal)", test_filter_between_decimal), + ("filter_between (int)", test_filter_between_int), + ("filter_raw", test_filter_raw), + ("filter_in (ints)", test_filter_in_ints), + ("filter_in (strings)", test_filter_in_strings), + ("filter_in + filter_eq combined", test_filter_in_combined), + ("filter_not_in (ints)", test_filter_not_in_ints), + ("filter_not_between", test_filter_not_between), + # where() expression trees + ("where(eq)", test_where_eq), + ("where(& AND)", test_where_and), + ("where(| OR)", test_where_or), + ("where(~ NOT)", test_where_not), + ("where(nested (A|B) & C)", test_where_nested_and_or), + ("where(contains)", test_where_contains), + ("where(startswith)", test_where_startswith), + ("where(endswith)", test_where_endswith), + ("where(between)", test_where_between), + ("where(is_null)", test_where_is_null), + ("where(is_not_null)", test_where_is_not_null), + ("where(raw)", test_where_raw), + ("where(filter_in & gt)", test_where_filter_in), + ("where(not_in)", test_where_not_in), + ("where(not_between)", test_where_not_between), + # Combined + ("combined fluent + where", test_combined_fluent_and_where), + ("combined multiple fluent", test_combined_multiple_fluent), + # Select, ordering, paging + ("select columns", test_select_columns), + ("order_by asc", test_order_by_asc), + ("order_by desc", test_order_by_desc), + ("top()", test_top), + ("page_size flat", test_page_size_flat), + ("page_size by_page", test_page_size_by_page), + ("page_size by_page + filter", test_page_size_by_page_with_filter), + # Edge cases + ("no results", test_no_results), + ("no filters", test_no_filters), + ("chaining order", test_chaining_order), +] + + +# --------------------------------------------------------------------------- +# MAIN +# --------------------------------------------------------------------------- + +def main(): + print("=" * 80) + print("QueryBuilder Comprehensive Live Tests (self-contained)") + print("=" * 80) + + base_url = ( + sys.argv[1] + if len(sys.argv) > 1 + else input("Enter Dataverse org URL (e.g. https://yourorg.crm.dynamics.com): ").strip() + ) + if not base_url: + print("No URL provided; exiting.") + sys.exit(1) + + base_url = base_url.rstrip("/") + credential = InteractiveBrowserCredential() + + ids_dict = {} + with DataverseClient(base_url=base_url, credential=credential) as client: + print(f"Connected to: {base_url}") + + try: + # Setup: create table + seed data + ids_dict = setup(client) + + # Run all tests + print(f"\nRunning {len(ALL_TESTS)} tests against '{TABLE}'...\n") + for name, fn in ALL_TESTS: + test(name, fn, client) + finally: + # Always teardown, even if tests fail + teardown(client, ids_dict) + + print("\n" + "=" * 80) + print(f"Results: {PASSED} passed, {FAILED} failed, {SKIPPED} skipped out of {PASSED + FAILED + SKIPPED}") + print("=" * 80) + + if ERRORS: + print("\nFailed tests:") + for name, ex in ERRORS: + print(f" - {name}: {ex}") + + sys.exit(1 if FAILED else 0) + + +if __name__ == "__main__": + main() diff --git a/forge.txt b/forge.txt new file mode 100644 index 00000000..64198584 --- /dev/null +++ b/forge.txt @@ -0,0 +1,557 @@ +Python Server-Side Extension SDK/Package + +We propose a Python server-side extension package that enables customers, agents, and first-party teams to author and run Dataverse server-side logic entirely in Python. This plays a role similar to the XRM SDK used today for developing and deploying Dataverse plug-ins in C#. + +SDK Capabilities + +The Python server-side extension package provides the following capabilities: + +Bring the Dataverse Entity model into Python as strongly typed classes + +Support code-generated, schema-aligned entity types with typed fields, relationships, and option sets + +Provide a SQL-like fluent query and execution API for server-side logic + +Generate Python entity Types directly from a live Dataverse environment + +Comparison to C# plugin Model + +In today's C# plugin model, customers use the IOrganizationService provided through the plugin execution context to query Dataverse using APIs such as QueryExpression, FetchXml, and QueryByAttributes. + +In the Python model, the function execution context exposes an equivalent server-side extension that allows customers or agents to query Dataverse through a more SQL-like fluent API. Under the hood, this extension translates those operations into SDK messages and executes them on the platform using IOrganizationService. + +SDK Message using SQL-Like Fluent Query API + +In the Python model, the function execution context exposes a fluent query interface that allows server-side logic to read and write Dataverse data using a SQL-style API built directly on top of the generated entity types. There is no separate query language to learn — conditions are expressed as Python operator comparisons on typed fields, making invalid field names and type mismatches visible at authoring time. + +Under the hood the fluent API compiles queries to SQL and executes them via IOrganizationService on the Dataverse platform. The caller never writes raw SQL strings. + +Factory functions + +Function + +Returns + +Purpose + +select(*fields) + +SelectQuery[T] + +Start a SELECT statement. Chain .from_() and .where(). + +update(entity) + +UpdateQuery + +Start an UPDATE statement. Chain .set() and .where(). + +insert_into(entity) + +InsertQuery + +Start an INSERT statement. Chain .value(). + + + +Condition operators + +Conditions are formed by applying Python comparison operators directly to typed field descriptors. Multiple conditions are composed of & (AND) and | (OR). Multiple .where() calls are joined with AND at the top level. + +Python expression + +SQL equivalent + +Lead.statecode == 0 + +statecode = ? + +Lead.budgetamount >= 50_000 + +budgetamount >= ? + +Lead.companyname != "Acme" + +companyname != ? + +cond_a & cond_b + +(expr_a AND expr_b) + +cond_a | cond_b + +(expr_a OR expr_b) + +.where(a).where(b) + +WHERE a AND b + + + +Query results + +Every executed query returns a SqlResult[T] instance. The result is generically typed if a SELECT is bound to an entity class the rows are hydrated as instances of that class, not plain dicts. + +property / method + +Type + +Available on + +.records + +list[T] + +SELECT + +.first() + +T | None + +SELECT + +.id + +str | None + +INSERT (GUID of new record) + +.affected + +int + +UPDATE / DELETE + +len(result) + +int + +SELECT + +bool(result) + +bool + +SELECT (True if any rows) + + + +Composing complex conditions + +The & and | operators let you build arbitrarily nested predicates. Parentheses control grouping exactly as in SQL. + +from Types.lead import Lead + + + +# Simple AND — two .where() calls + +select(Lead.leadid) + + .from_(Lead) + + .where(Lead.statecode == 0) + + .where(Lead.budgetamount >= 10_000) + +# → WHERE statecode = ? AND budgetamount >= ? + + + +# OR within a single .where() + +select(Lead.leadid) + + .from_(Lead) + + .where((Lead.budgetamount >= 50_000) | (Lead.industrycode == 7)) + +# → WHERE (budgetamount >= ? OR industrycode = ?) + + + +# Mixed — OR group AND a separate mandatory filter + +select(Lead.leadid, Lead.companyname) + + .from_(Lead) + + .where((Lead.budgetamount >= 50_000) | (Lead.numberofemployees >= 100)) + + .where(Lead.statecode == 0) + +# → WHERE (budgetamount >= ? OR numberofemployees >= ?) AND statecode = ? + + + +End-to-end example — Lead qualification function + +The following example implements the AI agent scenario: evaluate an incoming Lead, update its status, and create the corresponding Account and Contact records. + +from functions import CoreDataverseFunction, FunctionContext + +from functions import select, update, insert_into + +from Types.lead import Lead + +from Types.account import Account + +from Types.contact import Contact + + + +class QualifyLeadFunction(CoreDataverseFunction): + + + + def PreValidation(self, ctx: FunctionContext) -> None: + + """Check whether the lead meets qualification criteria.""" + + + + # SELECT — fetch the incoming lead by id + + # Conditions use typed field operators; no raw SQL strings + + result = ctx.dataverse.sql( + + select(Lead.leadid, Lead.companyname, Lead.budgetamount, + + Lead.firstname, Lead.lastname, Lead.emailaddress1) + + .from_(Lead) + + .where(Lead.leadid == ctx.input["leadid"]) + + .where(Lead.statecode == 0) # open leads only + + ).execute() + + + + lead = result.first() + + if lead is None: + + raise ValueError(f"Lead {ctx.input['leadid']} not found or already closed.") + + + + # Fail fast if qualification criteria are not met + + if lead.budgetamount is None or lead.budgetamount < 10_000: + + raise ValueError( + + f"Lead {lead.leadid} does not meet budget threshold " + + f"(budget={lead.budgetamount})." + + ) + + + + # Pass the qualified lead to subsequent stages via shared context + + ctx.shared["lead"] = lead + + + + def PreOperation(self, ctx: FunctionContext) -> None: + + """Update lead status to Qualified.""" + + + + lead = ctx.shared["lead"] + + + + ctx.dataverse.sql( + + update(Lead) + + .set(Lead.statecode, 2) # Qualified + + .set(Lead.statuscode, 3) # Qualified reason + + .where(Lead.leadid == lead.leadid) + + ).execute() + + + + def PostOperation(self, ctx: FunctionContext) -> None: + + """Create Account and Contact from the qualified lead.""" + + + + lead = ctx.shared["lead"] + + + + # INSERT Account — returns id of the created record + + account_result = ctx.dataverse.sql( + + insert_into(Account) + + .value(Account.name, lead.companyname) + + .value(Account.originatingleadid, lead.leadid) + + ).execute() + + + + account_id = account_result.id # GUID of the new Account record + + + + # INSERT Contact linked to the new Account + + ctx.dataverse.sql( + + insert_into(Contact) + + .value(Contact.firstname, lead.firstname) + + .value(Contact.lastname, lead.lastname) + + .value(Contact.emailaddress1, lead.emailaddress1) + + .value(Contact.accountid, account_id) + + ).execute() + + + + ctx.output["account_id"] = account_id + +Entity Type Generator + +The SDK exposes a generate() function that connects to a live Dataverse environment, downloads entity metadata via the OData Web API, and writes strongly typed Python classes to a Types/ folder inside the workspace. + +Programmatic usage + +from pathlib import Path + +from azure.identity import InteractiveBrowserCredential + +from generator import generate + + + +generate( + + org_url = "https://yourorg.crm.dynamics.com", + + entities = ["account", "contact", "lead"], + + credential = InteractiveBrowserCredential(), + + output_dir = Path("src/runtime/python/sdk"), + +) + +CLI usage + +python tools/generate_entity.py \ + + --url https://yourorg.crm.dynamics.com \ + + --entities account contact lead + +The generator also automatically discovers and fetches dependencies: + +Lookup dependencies — entities referenced by Lookup fields (e.g. businessunit) fetched automatically + +M2M participants — both sides of many-to-many relationships fetched automatically + +OneToMany referencers — entities that have a Lookup back to this entity opt-in prompt + + + +Generated output structure + +sdk/Types/ + + account.py — Account(Entity) with typed fields + + contact.py + + lead.py + + picklists/ + + lead_leadsourcecode.py — local option set + + .py — global option set shared across entities + + __init__.py + + booleans/ + + .py — boolean (two-option) types + + __init__.py + + intersects/ + + .py — M2M intersect entity stubs + + __init__.py + + _auto_types.py — fallback descriptors for unknown attribute types + + __init__.py + + + +Python Type representation of the System User Entity + +The image displays a segment of a code file, specifically a Python class definition for a 'Systemuser' entity in a data model, with attributes such as `guid`, `accessmode`, `applicationid`, `azureactivedirectoryobjectid`, `azurestate`, and `businessunitid`. + +AI-generated content may be incorrect. + +Core Field Types + +Every field in a generated entity class is a typed descriptor backed by a core SDK type. The table below maps Dataverse attribute types to their SDK equivalents and Python value types. + +SDK Type + +Dataverse Attribute Type(s) + +Python Value Type + +Text + +String, File, Image + +str + +Memo + +Memo + +str + +Integer + +Integer + +int + +BigInt + +BigInt + +int + +DecimalNumber + +Decimal + +float + +Double + +Double + +float + +Money + +Money + +float + +DateTime + +DateTime + +str (ISO 8601) + +Guid + +Uniqueidentifier + +str (UUID) + +Lookup + +Lookup, Owner + +str (entity id) + +CustomerLookup + +Customer (polymorphic) + +str (entity id) + +Boolean + +Boolean (with named options) + +bool + +BooleanBase + +Boolean (no named options) + +bool + +Picklist + +Picklist + +int + +State + +State + +int + +Status + +Status + +int + +MultiPicklist + +Multiselectpicklist, PartyList + +str + + + +AI Agent Persona/Workflow + +A customer begins by creating a new Dataverse workspace using the DV Plugin/CLI with a business intent. For example: + +Evaluate whether an incoming Lead meets qualification criteria and, if so, update the Lead status and create the corresponding Account and Contact records. + +The AI agent then: + +Validates the availability of required Dataverse entities within the SDK + +Connects to the live Dataverse organization and generates strongly typed Python classes aligned to the schema — including fields, relationships, and option sets + +Retrieves the entity model + +python tools/generate_entity.py \ + + --url https://.crm.dynamics.com \ + + --entities account contact lead + +Represents each entity as a first-class Python type with schema-aligned attributes + +Uses these generated types to author the required server-side business logic + +The resulting function and dependent entities are packaged and deployed to Dataverse, where they are stored in the sdkmessageprocessingfunction entity analogous to how plug-ins are registered today via sdkmessageprocessingstep and executed securely within the Dataverse sandbox Python runtime. \ No newline at end of file diff --git a/functional_testing_log.txt b/functional_testing_log.txt new file mode 100644 index 00000000..c449a41b --- /dev/null +++ b/functional_testing_log.txt @@ -0,0 +1,191 @@ +PowerPlatform Dataverse Client SDK - Advanced Functional Testing +====================================================================== +This script tests SDK functionality in a real Dataverse environment: + - Authentication & Connection + - Table Creation & Metadata Operations + - Record CRUD Operations + - Query Functionality + - Relationship Operations (1:N, N:N, lookup, get, delete) + - Batch Operations (create, read, update, changeset, delete) + - Interactive Cleanup +====================================================================== +For installation validation, run examples/basic/installation_example.py first +====================================================================== + +-> Authentication Setup +================================================== + +-> Dataverse Environment Setup +================================================== +Testing connection... +C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\msal\oauth2cli\oauth2.py:407: UserWarning: response_mode='form_post' is recommended for better security. See https://www.rfc-editor.org/rfc/rfc9700.html#section-4.3.1 + warnings.warn( +[OK] Connection successful! Found 1556 tables. +[OK] Found 866 user-owned tables (filter + select). + +-> Test Table Setup +================================================== +Creating new test table... +[OK] Created test table: test_TestSDKFunctionality + Logical name: test_testsdkfunctionality + Entity set: test_testsdkfunctionalities + +-> Record Creation Test +================================================== +Creating test record... +[OK] Record created successfully! + Record ID: 11179b49-1d53-f111-a821-00224808976e + Name: Test Record 17:54:33 + +-> Record Reading Test +================================================== +Reading record: 11179b49-1d53-f111-a821-00224808976e +[OK] Record retrieved successfully! + Retrieved data: + test_name: Test Record 17:54:33 + test_description: This is a test record created by the SDK functionality test + test_count: 42 + test_amount: 123.45 + test_is_active: True +[OK] include_annotations verified: test_is_active@OData.Community.Display.V1.FormattedValue = 'True' +[OK] records.retrieve with expand=['owninguser']: owner='Aurora365 User1' + +-> Record Query Test +================================================== +Querying records with records.list()... + Record 1: Test Record 17:54:33 (Count: 42, Amount: 123.45) +[OK] records.list() completed! Found 1 active records. + +Querying records with records.list_pages() (paged)... + Page 1: 1 record(s) — ['Test Record 17:54:33'] +[OK] records.list_pages() completed! 1 records across 1 page(s). + +Querying records.list() with orderby / page_size / count / include_annotations... + Records (ordered): ['Test Record 17:54:33'] +[OK] include_annotations verified: 'test_is_active@OData.Community.Display.V1.FormattedValue' present in list() results +[OK] records.list() with extended params completed! 1 record(s). + +Querying records.list_pages() with orderby / page_size / include_annotations... +[OK] include_annotations verified in list_pages() results +[OK] records.list_pages() with extended params completed! 1 record(s). + +-> Relationship Tests +================================================== +Checking for leftover relationship test resources... + +Creating relationship test tables... +[OK] Created parent table: test_RelParent +[OK] Created child table: test_RelChild +[OK] Created M:N table: test_RelProject + + Test 1: Create 1:N relationship (core API) + --------------------------------------------- + [OK] Created 1:N relationship: test_RelParent_RelChild + Lookup: test_ParentId + ID: d5abd075-1d53-f111-a821-00224808976e + + Test 2: Create lookup field (convenience API) + --------------------------------------------- + [OK] Created lookup: test_ManagerId + Relationship: contact_test_relchild_test_ManagerId + + Test 3: Create N:N relationship + --------------------------------------------- + [OK] Created N:N relationship: test_relchild_relproject + ID: 9f10f78a-1d53-f111-a821-00224808976e + + Test 4: Query relationship metadata + --------------------------------------------- + [OK] Retrieved 1:N: test_RelParent_RelChild + Referenced: test_relparent + Referencing: test_relchild + [OK] Retrieved N:N: test_relchild_relproject + Entity1: test_relchild + Entity2: test_relproject + [OK] Non-existent relationship returns None + + Test 5: Delete relationships + --------------------------------------------- + [OK] Deleted 1:N relationship + [OK] Deleted lookup relationship + [OK] Deleted N:N relationship + [OK] Verified 1:N deletion (get returns None) + +[OK] All relationship tests passed! + (Cleaned up table: test_RelProject) + (Cleaned up table: test_RelChild) + (Cleaned up table: test_RelParent) + +-> SQL Encoding Verification Test +================================================== + [1/3] Basic SELECT (no special chars): SELECT TOP 5 test_name FROM test_testsdkfunctionality + [OK] Both paths returned 1 rows + [2/3] WHERE with spaces/colons: ...WHERE test_name = 'Test Record 17:54:33' + [OK] Both paths found the record: 'Test Record 17:54:33' + [3/3] WHERE with '=' in string literal (tests %3D encoding) + [OK] Both paths found record with '=' in name: 'SQL=Test 18:00:34' +[OK] SQL encoding verification passed — %20/%3D encoding is consistent across both paths + +-> Batch Operations Test (All Operations) +================================================== + +[1/11] Create — single + CreateMultiple (2 ops, 1 POST $batch) +[OK] 2 ops → 1 records created: ['4fc0b923-1e53-f111-a821-00224808976e'] + +[2/11] Read — records.retrieve + records.list(orderby/page_size/count/include_annotations) + tables.get + tables.list + query.sql (5 ops, 1 POST $batch) +[OK] 5 succeeded, 0 failed + records.retrieve → name='Batch-A 18:00:38', test_is_active@OData.Community.Display.V1.FormattedValue='True' + records.retrieve expand=['owninguser'] → owner='Aurora365 User1' + records.list → 4 row(s), ordered: ['Batch-A 18:00:38', 'Batch-B 18:00:38', 'Batch-C 18:00:38', 'Test Record 17:54:33'] + [OK] include_annotations 'test_is_active@OData.Community.Display.V1.FormattedValue' present in batch.records.list() results + tables.get → LogicalName='None', EntitySet='None' + tables.list → 1557 tables returned + query.sql → 4 rows returned + +[4/11] Changeset (happy path) — cs.create + cs.update(ref) + cs.delete (1 transaction) +[OK] 3 ops committed atomically (create + update + delete) + +[5/11] Changeset (rollback) — cs.create + cs.update(nonexistent) → full rollback +[OK] Changeset rollback verified: changeset failed, no records created + +[6/11] Two changesets in one batch — globally unique Content-IDs across changesets +[OK] Both changesets committed — 4 records created with globally unique Content-IDs across changesets: ['7da0a52a-1e53-f111-a821-00224808976e', '7da0a52a-1e53-f111-a821-00224808976e', '7ea0a52a-1e53-f111-a821-00224808976e', '7ea0a52a-1e53-f111-a821-00224808976e'] + +[7/11] Content-ID reference chaining — two creates + two updates via $n refs +[OK] Both records created and updated via content-ID refs $1 and $2: ['7fa0a52a-1e53-f111-a821-00224808976e', '80a0a52a-1e53-f111-a821-00224808976e', '7fa0a52a-1e53-f111-a821-00224808976e', '80a0a52a-1e53-f111-a821-00224808976e'] + +[8/11] Batch tables.add_columns — two add-column requests in one batch +[OK] 2 column(s) added via batch: test_batch_extra_a, test_batch_extra_b +[OK] Removed 2 batch-added column(s) via batch.tables.remove_columns + +[9/11] Upsert — UpsertItem with alternate key (expected to fail: no alt key on test table) +[WARN] Upsert failed as expected (no alternate key configured): 400 + +[10/11] Mixed batch (continue_on_error=True) — 1 bad get + 1 good get +examples/basic/functional_testing.py:874: DeprecationWarning: 'batch.records.get()' is deprecated; use 'batch.records.retrieve(table, record_id)' instead. + batch.records.get( +examples/basic/functional_testing.py:879: DeprecationWarning: 'batch.records.get()' is deprecated; use 'batch.records.retrieve(table, record_id)' instead. + batch.records.get( +[OK] Succeeded: 1, Failed: 1 + Expected failure: 404 Entity 'test_testsdkfunctionality' With Id = 00000000-0000-0000-0000-000000000002 Does Not Exist + +[11/11] Delete — 9 records via multi-delete (use_bulk_delete=False, 1 POST $batch) +[OK] Deleted 5, failed 4 + +[OK] Batch all-operations test completed! + +Functional Test Summary +================================================== +[OK] Authentication: Success +[OK] Table Operations: Success +[OK] Record Creation: Success +[OK] Record Reading: Success +[OK] Record Querying: Success +[OK] Relationship Operations: Success +[OK] SQL Encoding: Success +[OK] Batch Operations: Success + +Your PowerPlatform Dataverse Client SDK is fully functional! + +-> Cleanup +================================================== diff --git a/functional_testing_output.txt b/functional_testing_output.txt new file mode 100644 index 00000000..59a99c86 --- /dev/null +++ b/functional_testing_output.txt @@ -0,0 +1,24 @@ +********************** +PowerShell transcript start +Start time: 20260518173652 +Username: REDMOND\abelmilash +RunAs User: REDMOND\abelmilash +Configuration Name: +Machine: LAPTOP-TTK1RHIJ (Microsoft Windows NT 10.0.26100.0) +Host Application: C:\Program Files\WindowsApps\Microsoft.PowerShell_7.6.1.0_x64__8wekyb3d8bbwe\pwsh.dll -noexit -command try { . "c:\Users\abelmilash\AppData\Local\Programs\Microsoft VS Code\07ff9d6178\resources\app\out\vs\workbench\contrib\terminal\common\scripts\shellIntegration.ps1" } catch {} +Process ID: 25756 +PSVersion: 7.6.1 +PSEdition: Core +GitCommitId: 7.6.1 +OS: Microsoft Windows 10.0.26100 +Platform: Win32NT +PSCompatibleVersions: 1.0, 2.0, 3.0, 4.0, 5.0, 5.1, 6.0, 7.0 +PSRemotingProtocolVersion: 2.4 +SerializationVersion: 1.1.0.1 +WSManStackVersion: 3.0 +********************** +Transcript started, output file is C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\functional_testing_output.txt +[OK] Test record deleted successfully +Do you want to delete the test table? (y/N): y +[OK] Test table deleted successfully +]633;D;0]633;A]633;P;Cwd=C:\x5cUsers\x5cabelmilash\x5cRepos\x5cPowerPlatform-DataverseClient-PythonPS C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python> ]633;B diff --git a/logs/log.txt b/logs/log.txt new file mode 100644 index 00000000..ba3155c9 --- /dev/null +++ b/logs/log.txt @@ -0,0 +1,245 @@ +INFO - __main__ - Adding yaml extension to path +INFO - py2docfx.convert_prepare.environment - : Creating basevenv... +INFO - py2docfx.convert_prepare.environment - : Installing converter requirements in ... +INFO - py2docfx.convert_prepare.pip_utils - Collecting setuptools<81 + Downloading setuptools-80.10.2-py3-none-any.whl.metadata (6.6 kB) +Collecting wheel + Using cached wheel-0.47.0-py3-none-any.whl.metadata (2.3 kB) +Collecting vcs-versioning + Downloading vcs_versioning-1.1.1-py3-none-any.whl.metadata (1.6 kB) +Collecting packaging>=24.0 (from wheel) + Using cached packaging-26.2-py3-none-any.whl.metadata (3.5 kB) +Downloading setuptools-80.10.2-py3-none-any.whl (1.1 MB) + ---------------------------------------- 1.1/1.1 MB 2.9 MB/s eta 0:00:00 +Using cached wheel-0.47.0-py3-none-any.whl (32 kB) +Downloading vcs_versioning-1.1.1-py3-none-any.whl (79 kB) +Using cached packaging-26.2-py3-none-any.whl (100 kB) +Installing collected packages: setuptools, packaging, wheel, vcs-versioning +Successfully installed packaging-26.2 setuptools-80.10.2 vcs-versioning-1.1.1 wheel-0.47.0 + +INFO - py2docfx.convert_prepare.git - : Cloning repo: .... +INFO - py2docfx.convert_prepare.git - Using empty branch of ., going to use master branch instead. +INFO - py2docfx.convert_prepare.git - Using master branch of ., going to use main branch instead. +INFO - py2docfx.convert_prepare.git - : Repo . successfully cloned in C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0... +INFO - py2docfx.convert_prepare.environment - : Installing required packages in basevenv... +INFO - py2docfx.convert_prepare.environment - : basevenv setup complete. +INFO - __main__ - Processing package PowerPlatform-Dataverse-Client, env_prepare_tasks: 1 +INFO - py2docfx.convert_prepare.pip_utils - Processing c:\users\abelmilash\repos\powerplatform-dataverseclient-python\.conda\lib\site-packages\py2docfx\source_repo\0 + Installing build dependencies: started + Installing build dependencies: finished with status 'done' + Getting requirements to build wheel: started + Getting requirements to build wheel: finished with status 'done' + Preparing metadata (pyproject.toml): started + Preparing metadata (pyproject.toml): finished with status 'done' +Collecting azure-identity>=1.17.0 (from PowerPlatform-Dataverse-Client==0.1.0b11) + Using cached azure_identity-1.25.3-py3-none-any.whl.metadata (91 kB) +Collecting azure-core>=1.30.2 (from PowerPlatform-Dataverse-Client==0.1.0b11) + Using cached azure_core-1.41.0-py3-none-any.whl.metadata (49 kB) +Collecting requests>=2.32.0 (from PowerPlatform-Dataverse-Client==0.1.0b11) + Using cached requests-2.34.2-py3-none-any.whl.metadata (4.8 kB) +Collecting pandas>=2.0.0 (from PowerPlatform-Dataverse-Client==0.1.0b11) + Using cached pandas-3.0.3-cp312-cp312-win_amd64.whl.metadata (19 kB) +Collecting typing-extensions>=4.6.0 (from azure-core>=1.30.2->PowerPlatform-Dataverse-Client==0.1.0b11) + Using cached typing_extensions-4.15.0-py3-none-any.whl.metadata (3.3 kB) +Collecting cryptography>=2.5 (from azure-identity>=1.17.0->PowerPlatform-Dataverse-Client==0.1.0b11) + Using cached cryptography-48.0.0-cp311-abi3-win_amd64.whl.metadata (4.3 kB) +Collecting msal>=1.35.1 (from azure-identity>=1.17.0->PowerPlatform-Dataverse-Client==0.1.0b11) + Using cached msal-1.36.0-py3-none-any.whl.metadata (11 kB) +Collecting msal-extensions>=1.2.0 (from azure-identity>=1.17.0->PowerPlatform-Dataverse-Client==0.1.0b11) + Using cached msal_extensions-1.3.1-py3-none-any.whl.metadata (7.8 kB) +Collecting numpy>=1.26.0 (from pandas>=2.0.0->PowerPlatform-Dataverse-Client==0.1.0b11) + Using cached numpy-2.4.6-cp312-cp312-win_amd64.whl.metadata (6.6 kB) +Collecting python-dateutil>=2.8.2 (from pandas>=2.0.0->PowerPlatform-Dataverse-Client==0.1.0b11) + Using cached python_dateutil-2.9.0.post0-py2.py3-none-any.whl.metadata (8.4 kB) +Collecting tzdata (from pandas>=2.0.0->PowerPlatform-Dataverse-Client==0.1.0b11) + Using cached tzdata-2026.2-py2.py3-none-any.whl.metadata (1.4 kB) +Collecting charset_normalizer<4,>=2 (from requests>=2.32.0->PowerPlatform-Dataverse-Client==0.1.0b11) + Using cached charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl.metadata (41 kB) +Collecting idna<4,>=2.5 (from requests>=2.32.0->PowerPlatform-Dataverse-Client==0.1.0b11) + Using cached idna-3.15-py3-none-any.whl.metadata (7.7 kB) +Collecting urllib3<3,>=1.26 (from requests>=2.32.0->PowerPlatform-Dataverse-Client==0.1.0b11) + Using cached urllib3-2.7.0-py3-none-any.whl.metadata (6.9 kB) +Collecting certifi>=2023.5.7 (from requests>=2.32.0->PowerPlatform-Dataverse-Client==0.1.0b11) + Using cached certifi-2026.5.20-py3-none-any.whl.metadata (2.5 kB) +Collecting cffi>=2.0.0 (from cryptography>=2.5->azure-identity>=1.17.0->PowerPlatform-Dataverse-Client==0.1.0b11) + Using cached cffi-2.0.0-cp312-cp312-win_amd64.whl.metadata (2.6 kB) +Collecting PyJWT<3,>=1.0.0 (from PyJWT[crypto]<3,>=1.0.0->msal>=1.35.1->azure-identity>=1.17.0->PowerPlatform-Dataverse-Client==0.1.0b11) + Using cached pyjwt-2.12.1-py3-none-any.whl.metadata (4.1 kB) +Collecting six>=1.5 (from python-dateutil>=2.8.2->pandas>=2.0.0->PowerPlatform-Dataverse-Client==0.1.0b11) + Using cached six-1.17.0-py2.py3-none-any.whl.metadata (1.7 kB) +Collecting pycparser (from cffi>=2.0.0->cryptography>=2.5->azure-identity>=1.17.0->PowerPlatform-Dataverse-Client==0.1.0b11) + Using cached pycparser-3.0-py3-none-any.whl.metadata (8.2 kB) +Using cached azure_core-1.41.0-py3-none-any.whl (220 kB) +Using cached azure_identity-1.25.3-py3-none-any.whl (192 kB) +Using cached pandas-3.0.3-cp312-cp312-win_amd64.whl (9.8 MB) +Using cached requests-2.34.2-py3-none-any.whl (73 kB) +Using cached certifi-2026.5.20-py3-none-any.whl (134 kB) +Using cached charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl (159 kB) +Using cached cryptography-48.0.0-cp311-abi3-win_amd64.whl (3.8 MB) +Using cached idna-3.15-py3-none-any.whl (72 kB) +Using cached msal-1.36.0-py3-none-any.whl (121 kB) +Using cached msal_extensions-1.3.1-py3-none-any.whl (20 kB) +Using cached numpy-2.4.6-cp312-cp312-win_amd64.whl (12.3 MB) +Using cached python_dateutil-2.9.0.post0-py2.py3-none-any.whl (229 kB) +Using cached typing_extensions-4.15.0-py3-none-any.whl (44 kB) +Using cached urllib3-2.7.0-py3-none-any.whl (131 kB) +Using cached tzdata-2026.2-py2.py3-none-any.whl (349 kB) +Using cached cffi-2.0.0-cp312-cp312-win_amd64.whl (183 kB) +Using cached pyjwt-2.12.1-py3-none-any.whl (29 kB) +Using cached six-1.17.0-py2.py3-none-any.whl (11 kB) +Using cached pycparser-3.0-py3-none-any.whl (48 kB) +Building wheels for collected packages: PowerPlatform-Dataverse-Client + Building wheel for PowerPlatform-Dataverse-Client (pyproject.toml): started + Building wheel for PowerPlatform-Dataverse-Client (pyproject.toml): finished with status 'done' + Created wheel for PowerPlatform-Dataverse-Client: filename=powerplatform_dataverse_client-0.1.0b11-py3-none-any.whl size=161972 sha256=733aa96924b44326771251c46d77ecca31ad94f11165fd1b3e50004d5f728b8e + Stored in directory: C:\Users\abelmilash\AppData\Local\Temp\pip-ephem-wheel-cache-oqd6t93_\wheels\62\fb\bd\36436e7b52d8c20c8ed21d1a9ebe6261f7ddca751551c08661 +Successfully built PowerPlatform-Dataverse-Client +Installing collected packages: urllib3, tzdata, typing-extensions, six, PyJWT, pycparser, numpy, idna, charset_normalizer, certifi, requests, python-dateutil, cffi, pandas, cryptography, azure-core, msal, msal-extensions, azure-identity, PowerPlatform-Dataverse-Client +Successfully installed PowerPlatform-Dataverse-Client-0.1.0b11 PyJWT-2.12.1 azure-core-1.41.0 azure-identity-1.25.3 certifi-2026.5.20 cffi-2.0.0 charset_normalizer-3.4.7 cryptography-48.0.0 idna-3.15 msal-1.36.0 msal-extensions-1.3.1 numpy-2.4.6 pandas-3.0.3 pycparser-3.0 python-dateutil-2.9.0.post0 requests-2.34.2 six-1.17.0 typing-extensions-4.15.0 tzdata-2026.2 urllib3-2.7.0 + +INFO - py2docfx.convert_prepare.environment - : venv0 setup complete. +INFO - __main__ - Adding yaml extension to path +INFO - py2docfx.convert_prepare.environment - : Creating basevenv... +INFO - py2docfx.convert_prepare.environment - : Installing converter requirements in ... +INFO - py2docfx.convert_prepare.pip_utils - Collecting setuptools<81 + Using cached setuptools-80.10.2-py3-none-any.whl.metadata (6.6 kB) +Collecting wheel + Using cached wheel-0.47.0-py3-none-any.whl.metadata (2.3 kB) +Collecting vcs-versioning + Using cached vcs_versioning-1.1.1-py3-none-any.whl.metadata (1.6 kB) +Collecting packaging>=24.0 (from wheel) + Using cached packaging-26.2-py3-none-any.whl.metadata (3.5 kB) +Using cached setuptools-80.10.2-py3-none-any.whl (1.1 MB) +Using cached wheel-0.47.0-py3-none-any.whl (32 kB) +Using cached vcs_versioning-1.1.1-py3-none-any.whl (79 kB) +Using cached packaging-26.2-py3-none-any.whl (100 kB) +Installing collected packages: setuptools, packaging, wheel, vcs-versioning +Successfully installed packaging-26.2 setuptools-80.10.2 vcs-versioning-1.1.1 wheel-0.47.0 + +ERROR - py2docfx.convert_prepare.pip_utils - Pip download failed with a non-retriable error. Exit code: 2. +ERROR - py2docfx.convert_prepare.pip_utils - STDOUT: Processing c:\users\abelmilash\repos\powerplatform-dataverseclient-python\.conda\lib\site-packages\py2docfx\dist\powerplatform_dataverse_client-0.1.0b11-py3-none-any.whl + +ERROR - py2docfx.convert_prepare.pip_utils - STDERR: WARNING: Requirement 'dist/powerplatform_dataverse_client-0.1.0b11-py3-none-any.whl' looks like a filename, but the file does not exist +ERROR: Exception: +Traceback (most recent call last): + File "C:\Users\abelmilash\AppData\Local\anaconda3\Lib\site-packages\pip\_internal\cli\base_command.py", line 107, in _run_wrapper + status = _inner_run() + File "C:\Users\abelmilash\AppData\Local\anaconda3\Lib\site-packages\pip\_internal\cli\base_command.py", line 98, in _inner_run + return self.run(options, args) + ~~~~~~~~^^^^^^^^^^^^^^^ + File "C:\Users\abelmilash\AppData\Local\anaconda3\Lib\site-packages\pip\_internal\cli\req_command.py", line 85, in wrapper + return func(self, options, args) + File "C:\Users\abelmilash\AppData\Local\anaconda3\Lib\site-packages\pip\_internal\commands\download.py", line 128, in run + requirement_set = resolver.resolve(reqs, check_supported_wheels=True) + File "C:\Users\abelmilash\AppData\Local\anaconda3\Lib\site-packages\pip\_internal\resolution\resolvelib\resolver.py", line 79, in resolve + collected = self.factory.collect_root_requirements(root_reqs) + File "C:\Users\abelmilash\AppData\Local\anaconda3\Lib\site-packages\pip\_internal\resolution\resolvelib\factory.py", line 538, in collect_root_requirements + reqs = list( + self._make_requirements_from_install_req( + ...<2 lines>... + ) + ) + File "C:\Users\abelmilash\AppData\Local\anaconda3\Lib\site-packages\pip\_internal\resolution\resolvelib\factory.py", line 494, in _make_requirements_from_install_req + cand = self._make_base_candidate_from_link( + ireq.link, + ...<2 lines>... + version=None, + ) + File "C:\Users\abelmilash\AppData\Local\anaconda3\Lib\site-packages\pip\_internal\resolution\resolvelib\factory.py", line 226, in _make_base_candidate_from_link + self._link_candidate_cache[link] = LinkCandidate( + ~~~~~~~~~~~~~^ + link, + ^^^^^ + ...<3 lines>... + version=version, + ^^^^^^^^^^^^^^^^ + ) + ^ + File "C:\Users\abelmilash\AppData\Local\anaconda3\Lib\site-packages\pip\_internal\resolution\resolvelib\candidates.py", line 318, in __init__ + super().__init__( + ~~~~~~~~~~~~~~~~^ + link=link, + ^^^^^^^^^^ + ...<4 lines>... + version=version, + ^^^^^^^^^^^^^^^^ + ) + ^ + File "C:\Users\abelmilash\AppData\Local\anaconda3\Lib\site-packages\pip\_internal\resolution\resolvelib\candidates.py", line 161, in __init__ + self.dist = self._prepare() + ~~~~~~~~~~~~~^^ + File "C:\Users\abelmilash\AppData\Local\anaconda3\Lib\site-packages\pip\_internal\resolution\resolvelib\candidates.py", line 238, in _prepare + dist = self._prepare_distribution() + File "C:\Users\abelmilash\AppData\Local\anaconda3\Lib\site-packages\pip\_internal\resolution\resolvelib\candidates.py", line 329, in _prepare_distribution + return preparer.prepare_linked_requirement(self._ireq, parallel_builds=True) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "C:\Users\abelmilash\AppData\Local\anaconda3\Lib\site-packages\pip\_internal\operations\prepare.py", line 543, in prepare_linked_requirement + return self._prepare_linked_requirement(req, parallel_builds) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^ + File "C:\Users\abelmilash\AppData\Local\anaconda3\Lib\site-packages\pip\_internal\operations\prepare.py", line 648, in _prepare_linked_requirement + hash = hash_file(local_file.path)[0].hexdigest() + ~~~~~~~~~^^^^^^^^^^^^^^^^^ + File "C:\Users\abelmilash\AppData\Local\anaconda3\Lib\site-packages\pip\_internal\utils\misc.py", line 614, in hash_file + with open(path, "rb") as f: + ~~~~^^^^^^^^^^^^ +FileNotFoundError: [Errno 2] No such file or directory: 'C:\\Users\\abelmilash\\Repos\\PowerPlatform-DataverseClient-Python\\.conda\\Lib\\site-packages\\py2docfx\\dist\\powerplatform_dataverse_client-0.1.0b11-py3-none-any.whl' + +INFO - py2docfx.convert_prepare.environment - : Installing required packages in basevenv... +INFO - py2docfx.convert_prepare.environment - : basevenv setup complete. +INFO - __main__ - Processing package None, env_prepare_tasks: 1 +ERROR - __main__ - Failed to setup venv for package None: Command '['pip', 'download', '--dest', 'C:\\Users\\abelmilash\\Repos\\PowerPlatform-DataverseClient-Python\\.conda\\Lib\\site-packages\\py2docfx\\dist_temp\\0', '--no-deps', 'dist/powerplatform_dataverse_client-0.1.0b11-py3-none-any.whl', '--prefer-binary']' returned non-zero exit status 2. +ERROR - __main__ - An error occurred: Command '['pip', 'download', '--dest', 'C:\\Users\\abelmilash\\Repos\\PowerPlatform-DataverseClient-Python\\.conda\\Lib\\site-packages\\py2docfx\\dist_temp\\0', '--no-deps', 'dist/powerplatform_dataverse_client-0.1.0b11-py3-none-any.whl', '--prefer-binary']' returned non-zero exit status 2. +INFO - __main__ - Adding yaml extension to path +INFO - py2docfx.convert_prepare.environment - : Creating basevenv... +INFO - py2docfx.convert_prepare.environment - : Installing converter requirements in ... +INFO - py2docfx.convert_prepare.pip_utils - Collecting setuptools<81 + Using cached setuptools-80.10.2-py3-none-any.whl.metadata (6.6 kB) +Collecting wheel + Using cached wheel-0.47.0-py3-none-any.whl.metadata (2.3 kB) +Collecting vcs-versioning + Using cached vcs_versioning-1.1.1-py3-none-any.whl.metadata (1.6 kB) +Collecting packaging>=24.0 (from wheel) + Using cached packaging-26.2-py3-none-any.whl.metadata (3.5 kB) +Using cached setuptools-80.10.2-py3-none-any.whl (1.1 MB) +Using cached wheel-0.47.0-py3-none-any.whl (32 kB) +Using cached vcs_versioning-1.1.1-py3-none-any.whl (79 kB) +Using cached packaging-26.2-py3-none-any.whl (100 kB) +Installing collected packages: setuptools, packaging, wheel, vcs-versioning +Successfully installed packaging-26.2 setuptools-80.10.2 vcs-versioning-1.1.1 wheel-0.47.0 + +INFO - py2docfx.convert_prepare.pip_utils - Processing c:\users\abelmilash\repos\powerplatform-dataverseclient-python\dist\powerplatform_dataverse_client-0.1.0b11-py3-none-any.whl +Saved c:\users\abelmilash\repos\powerplatform-dataverseclient-python\.conda\lib\site-packages\py2docfx\dist_temp\0\powerplatform_dataverse_client-0.1.0b11-py3-none-any.whl +Successfully downloaded powerplatform-dataverse-client + +INFO - py2docfx.convert_prepare.environment - : Installing required packages in basevenv... +INFO - py2docfx.convert_prepare.environment - : basevenv setup complete. +INFO - __main__ - Processing package None, env_prepare_tasks: 1 +ERROR - __main__ - Failed to setup venv for package None: join() argument must be str, bytes, or os.PathLike object, not 'NoneType' +ERROR - __main__ - An error occurred: join() argument must be str, bytes, or os.PathLike object, not 'NoneType' +INFO - __main__ - Adding yaml extension to path +INFO - py2docfx.convert_prepare.environment - : Creating basevenv... +INFO - py2docfx.convert_prepare.environment - : Installing converter requirements in ... +INFO - py2docfx.convert_prepare.pip_utils - Collecting setuptools<81 + Using cached setuptools-80.10.2-py3-none-any.whl.metadata (6.6 kB) +Collecting wheel + Using cached wheel-0.47.0-py3-none-any.whl.metadata (2.3 kB) +Collecting vcs-versioning + Using cached vcs_versioning-1.1.1-py3-none-any.whl.metadata (1.6 kB) +Collecting packaging>=24.0 (from wheel) + Using cached packaging-26.2-py3-none-any.whl.metadata (3.5 kB) +Using cached setuptools-80.10.2-py3-none-any.whl (1.1 MB) +Using cached wheel-0.47.0-py3-none-any.whl (32 kB) +Using cached vcs_versioning-1.1.1-py3-none-any.whl (79 kB) +Using cached packaging-26.2-py3-none-any.whl (100 kB) +Installing collected packages: setuptools, packaging, wheel, vcs-versioning +Successfully installed packaging-26.2 setuptools-80.10.2 vcs-versioning-1.1.1 wheel-0.47.0 + +INFO - py2docfx.convert_prepare.pip_utils - Processing c:\users\abelmilash\repos\powerplatform-dataverseclient-python\dist\powerplatform_dataverse_client-0.1.0b11-py3-none-any.whl +Saved c:\users\abelmilash\repos\powerplatform-dataverseclient-python\.conda\lib\site-packages\py2docfx\dist_temp\0\powerplatform_dataverse_client-0.1.0b11-py3-none-any.whl +Successfully downloaded powerplatform-dataverse-client + +INFO - py2docfx.convert_prepare.environment - : Installing required packages in basevenv... +INFO - py2docfx.convert_prepare.environment - : basevenv setup complete. +INFO - __main__ - Processing package None, env_prepare_tasks: 1 +ERROR - __main__ - Failed to setup venv for package None: join() argument must be str, bytes, or os.PathLike object, not 'NoneType' +ERROR - __main__ - An error occurred: join() argument must be str, bytes, or os.PathLike object, not 'NoneType' diff --git a/logs/package_logs/None.txt b/logs/package_logs/None.txt new file mode 100644 index 00000000..1c60d257 --- /dev/null +++ b/logs/package_logs/None.txt @@ -0,0 +1,4 @@ +INFO - py2docfx.convert_prepare.pack - Unpacking to: C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\dist_temp\0\powerplatform_dataverse_client-0.1.0b11...OK + +INFO - py2docfx.convert_prepare.pack - Unpacking to: C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\dist_temp\0\powerplatform_dataverse_client-0.1.0b11...OK + diff --git a/logs/package_logs/PowerPlatform-Dataverse-Client.txt b/logs/package_logs/PowerPlatform-Dataverse-Client.txt new file mode 100644 index 00000000..b5f9e8c1 --- /dev/null +++ b/logs/package_logs/PowerPlatform-Dataverse-Client.txt @@ -0,0 +1,60 @@ +INFO - py2docfx.convert_prepare.generate_document - : Generating RST files for PowerPlatform-Dataverse-Client. +INFO - py2docfx.convert_prepare.sphinx_caller - : Subfolder path C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\.azdo. +INFO - py2docfx.convert_prepare.sphinx_caller - : Subfolder path C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\.claude. +INFO - py2docfx.convert_prepare.sphinx_caller - : Subfolder path C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\.github. +INFO - py2docfx.convert_prepare.sphinx_caller - : Subfolder path C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\build. +INFO - py2docfx.convert_prepare.sphinx_caller - : Subfolder path C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc. +INFO - py2docfx.convert_prepare.sphinx_caller - : Subfolder path C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\examples. +INFO - py2docfx.convert_prepare.sphinx_caller - Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\examples.rst. +Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\examples.advanced.rst. +Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\examples.basic.rst. + +INFO - py2docfx.convert_prepare.sphinx_caller - : Subfolder path C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\src. +INFO - py2docfx.convert_prepare.sphinx_caller - Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\src.rst. +Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\src.PowerPlatform.rst. +Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\src.PowerPlatform.Dataverse.rst. +Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\src.PowerPlatform.Dataverse.claude_skill.rst. +Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\src.PowerPlatform.Dataverse.common.rst. +Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\src.PowerPlatform.Dataverse.core.rst. +Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\src.PowerPlatform.Dataverse.data.rst. +Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\src.PowerPlatform.Dataverse.extensions.rst. +Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\src.PowerPlatform.Dataverse.migration.rst. +Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\src.PowerPlatform.Dataverse.models.rst. +Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\src.PowerPlatform.Dataverse.operations.rst. +Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\src.PowerPlatform.Dataverse.utils.rst. + +INFO - py2docfx.convert_prepare.sphinx_caller - : Subfolder path C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\tests. +INFO - py2docfx.convert_prepare.sphinx_caller - Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\tests.rst. +Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\tests.e2e.rst. +Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\tests.fixtures.rst. +Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\tests.unit.rst. +Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\tests.unit.core.rst. +Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\tests.unit.data.rst. +Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\tests.unit.models.rst. + +INFO - py2docfx.convert_prepare.generate_document - : Listing RST files: +INFO - py2docfx.convert_prepare.generate_document - examples.advanced.rst +INFO - py2docfx.convert_prepare.generate_document - examples.basic.rst +INFO - py2docfx.convert_prepare.generate_document - examples.rst +INFO - py2docfx.convert_prepare.generate_document - src.PowerPlatform.Dataverse.claude_skill.rst +INFO - py2docfx.convert_prepare.generate_document - src.PowerPlatform.Dataverse.common.rst +INFO - py2docfx.convert_prepare.generate_document - src.PowerPlatform.Dataverse.core.rst +INFO - py2docfx.convert_prepare.generate_document - src.PowerPlatform.Dataverse.data.rst +INFO - py2docfx.convert_prepare.generate_document - src.PowerPlatform.Dataverse.extensions.rst +INFO - py2docfx.convert_prepare.generate_document - src.PowerPlatform.Dataverse.migration.rst +INFO - py2docfx.convert_prepare.generate_document - src.PowerPlatform.Dataverse.models.rst +INFO - py2docfx.convert_prepare.generate_document - src.PowerPlatform.Dataverse.operations.rst +INFO - py2docfx.convert_prepare.generate_document - src.PowerPlatform.Dataverse.rst +INFO - py2docfx.convert_prepare.generate_document - src.PowerPlatform.Dataverse.utils.rst +INFO - py2docfx.convert_prepare.generate_document - src.PowerPlatform.rst +INFO - py2docfx.convert_prepare.generate_document - src.rst +INFO - py2docfx.convert_prepare.generate_document - tests.e2e.rst +INFO - py2docfx.convert_prepare.generate_document - tests.fixtures.rst +INFO - py2docfx.convert_prepare.generate_document - tests.rst +INFO - py2docfx.convert_prepare.generate_document - tests.unit.core.rst +INFO - py2docfx.convert_prepare.generate_document - tests.unit.data.rst +INFO - py2docfx.convert_prepare.generate_document - tests.unit.models.rst +INFO - py2docfx.convert_prepare.generate_document - tests.unit.rst +INFO - py2docfx.convert_prepare.generate_document - : Running Sphinx build... +INFO - py2docfx.convert_prepare.sphinx_caller - b'Running Sphinx v6.1.3\r\nmaking output directory... done\r\nbuilding [mo]: targets for 0 po files that are out of date\r\nwriting output... \r\nbuilding [yaml]: targets for 23 source files that are out of date\r\nupdating environment: [new config] 23 added, 0 changed, 0 removed\r\nreading sources... [ 4%] examples \rreading sources... [ 8%] examples.advanced \rreading sources... [ 13%] examples.basic \rreading sources... [ 17%] index \rreading sources... [ 21%] src \rreading sources... [ 26%] src.PowerPlatform \rreading sources... [ 30%] src.PowerPlatform.Dataverse \rreading sources... [ 34%] src.PowerPlatform.Dataverse.claude_skill \rreading sources... [ 39%] src.PowerPlatform.Dataverse.common \rreading sources... [ 43%] src.PowerPlatform.Dataverse.core \rreading sources... [ 47%] src.PowerPlatform.Dataverse.data \rreading sources... [ 52%] src.PowerPlatform.Dataverse.extensions \rreading sources... [ 56%] src.PowerPlatform.Dataverse.migration \rreading sources... [ 60%] src.PowerPlatform.Dataverse.models \rreading sources... [ 65%] src.PowerPlatform.Dataverse.operations \rreading sources... [ 69%] src.PowerPlatform.Dataverse.utils \rreading sources... [ 73%] tests \rreading sources... [ 78%] tests.e2e \rreading sources... [ 82%] tests.fixtures \rreading sources... [ 86%] tests.unit \rreading sources... [ 91%] tests.unit.core \rreading sources... [ 95%] tests.unit.data \rreading sources... [100%] tests.unit.models \r\r\nlooking for now-outdated files... none found\r\npickling environment... done\r\nchecking consistency... done\r\npreparing documents... done\r\nwriting output... [ 4%] examples \rwriting output... [ 8%] examples.advanced \rwriting output... [ 13%] examples.basic \rwriting output... [ 17%] index \rwriting output... [ 21%] src \rwriting output... [ 26%] src.PowerPlatform \rwriting output... [ 30%] src.PowerPlatform.Dataverse \rwriting output... [ 34%] src.PowerPlatform.Dataverse.claude_skill \rwriting output... [ 39%] src.PowerPlatform.Dataverse.common \rwriting output... [ 43%] src.PowerPlatform.Dataverse.core \rwriting output... [ 47%] src.PowerPlatform.Dataverse.data \rwriting output... [ 52%] src.PowerPlatform.Dataverse.extensions \rwriting output... [ 56%] src.PowerPlatform.Dataverse.migration \rwriting output... [ 60%] src.PowerPlatform.Dataverse.models \rwriting output... [ 65%] src.PowerPlatform.Dataverse.operations \rwriting output... [ 69%] src.PowerPlatform.Dataverse.utils \rwriting output... [ 73%] tests \rwriting output... [ 78%] tests.e2e \rwriting output... [ 82%] tests.fixtures \rwriting output... [ 86%] tests.unit \rwriting output... [ 91%] tests.unit.core \rwriting output... [ 95%] tests.unit.data \rwriting output... [100%] tests.unit.models \r\r\nbuild succeeded, 100 warnings.\r\n' +INFO - py2docfx.convert_prepare.sphinx_caller - b"WARNING: autodoc: failed to import module 'examples'; the following exception was raised:\r\nNo module named 'examples'\r\nWARNING: autodoc: failed to import module 'advanced' from module 'examples'; the following exception was raised:\r\nNo module named 'examples'\r\nWARNING: autodoc: failed to import module 'advanced.alternate_keys_upsert' from module 'examples'; the following exception was raised:\r\nNo module named 'examples'\r\nWARNING: autodoc: failed to import module 'advanced.batch' from module 'examples'; the following exception was raised:\r\nNo module named 'examples'\r\nWARNING: autodoc: failed to import module 'advanced.dataframe_operations' from module 'examples'; the following exception was raised:\r\nNo module named 'examples'\r\nWARNING: autodoc: failed to import module 'advanced.datascience_risk_assessment' from module 'examples'; the following exception was raised:\r\nNo module named 'examples'\r\nWARNING: autodoc: failed to import module 'advanced.fetchxml' from module 'examples'; the following exception was raised:\r\nNo module named 'examples'\r\nWARNING: autodoc: failed to import module 'advanced.file_upload' from module 'examples'; the following exception was raised:\r\nNo module named 'examples'\r\nWARNING: autodoc: failed to import module 'advanced.prodev_quick_start' from module 'examples'; the following exception was raised:\r\nNo module named 'examples'\r\nWARNING: autodoc: failed to import module 'advanced.relationships' from module 'examples'; the following exception was raised:\r\nNo module named 'examples'\r\nWARNING: autodoc: failed to import module 'advanced.sql_examples' from module 'examples'; the following exception was raised:\r\nNo module named 'examples'\r\nWARNING: autodoc: failed to import module 'advanced.walkthrough' from module 'examples'; the following exception was raised:\r\nNo module named 'examples'\r\nWARNING: autodoc: failed to import module 'basic' from module 'examples'; the following exception was raised:\r\nNo module named 'examples'\r\nWARNING: autodoc: failed to import module 'basic.functional_testing' from module 'examples'; the following exception was raised:\r\nNo module named 'examples'\r\nWARNING: autodoc: failed to import module 'basic.installation_example' from module 'examples'; the following exception was raised:\r\nNo module named 'examples'\r\nWARNING: autodoc: failed to import module 'PowerPlatform' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.client' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.claude_skill' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.common' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.common.constants' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.core' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.core.config' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.core.errors' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.core.log_config' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.data' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.extensions' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.migration' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.migration.migrate_v0_to_v1' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.models' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.models.batch' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.models.fetchxml_query' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.models.filters' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.models.labels' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.models.protocol' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.models.query_builder' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.models.record' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.models.relationship' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.models.table_info' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.models.upsert' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.operations' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.operations.batch' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.operations.dataframe' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.operations.files' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.operations.query' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.operations.records' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.operations.tables' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.utils' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'e2e' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.e2e'\r\nWARNING: autodoc: failed to import module 'e2e.test_relationships_e2e' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.e2e'\r\nWARNING: autodoc: failed to import module 'fixtures.test_data' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.fixtures'\r\nWARNING: autodoc: failed to import module 'unit' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.test_batch_dataframe' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.test_batch_operations' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.test_batch_scenarios' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.test_client' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.test_client_dataframe' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.test_client_deprecations' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.test_context_manager' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.test_dataframe_operations' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.test_files_operations' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.test_migration_tool' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.test_operation_context' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.test_pandas_helpers' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.test_phase1_ga' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.test_phase2_ga' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.test_phase3_ga' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.test_phase4_ga' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.test_query_operations' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.test_records_operations' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.test_tables_operations' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.core' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.core.test_auth' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.core.test_http_client' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.core.test_http_errors' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.core.test_http_logger' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.data' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.data.test_batch_edge_cases' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.data.test_batch_serialization' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.data.test_enum_optionset_payload' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.data.test_format_key' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.data.test_list_columns' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.data.test_logical_crud' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.data.test_odata_internal' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.data.test_relationships' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.data.test_sql_guardrails' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.data.test_sql_parse' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.data.test_upload' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.models' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.models.test_alternate_key' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.models.test_filters' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.models.test_labels' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.models.test_query_builder' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.models.test_record' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.models.test_relationship' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.models.test_relationship_info' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.models.test_table_info' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nC:\\Users\\abelmilash\\Repos\\PowerPlatform-DataverseClient-Python\\.conda\\Lib\\site-packages\\py2docfx\\source_repo\\0\\doc\\examples.rst: WARNING: document isn't included in any toctree\r\nC:\\Users\\abelmilash\\Repos\\PowerPlatform-DataverseClient-Python\\.conda\\Lib\\site-packages\\py2docfx\\source_repo\\0\\doc\\src.rst: WARNING: document isn't included in any toctree\r\nC:\\Users\\abelmilash\\Repos\\PowerPlatform-DataverseClient-Python\\.conda\\Lib\\site-packages\\py2docfx\\source_repo\\0\\doc\\tests.rst: WARNING: document isn't included in any toctree\r\n" diff --git a/scratch_async_demo.py b/scratch_async_demo.py new file mode 100644 index 00000000..c97b5b3c --- /dev/null +++ b/scratch_async_demo.py @@ -0,0 +1,82 @@ +""" +Demo: async-only SDK usage patterns and asyncio.run() behavior. + +Run with: + PYTHONPATH=src python scratch_async_demo.py +""" + +import asyncio + + +# --------------------------------------------------------------------------- +# Pretend this is an "async-only SDK" — no sync client exists +# --------------------------------------------------------------------------- + +async def sdk_create_record(table: str, data: dict) -> str: + """Simulated async-only SDK method.""" + await asyncio.sleep(0) # yields control, like a real network call + return f"fake-guid-for-{data.get('name', 'unknown')}" + + +# --------------------------------------------------------------------------- +# Part 1: How sync users must call an async-only SDK +# --------------------------------------------------------------------------- + +print("=" * 60) +print("PART 0: Calling async function directly (no await, no asyncio.run)") +print("=" * 60) + +result = sdk_create_record("account", {"name": "Contoso"}) +print(f"Return value: {result}") +print(f"Type: {type(result)}") +print("The coroutine was never executed — no network call happened.") +print() + +print("=" * 60) +print("PART 1: Sync usage of an async-only function") +print("=" * 60) + +# Sync user wraps every call in asyncio.run() +record_id = asyncio.run(sdk_create_record("account", {"name": "Contoso"})) +print(f"Created record: {record_id}") +print() +print("Sync call required:") +print(" asyncio.run(client.records.create('account', {'name': 'Contoso'}))") +print("Instead of the simpler sync SDK syntax:") +print(" client.records.create('account', {'name': 'Contoso'})") +print() + + +# --------------------------------------------------------------------------- +# Part 2: asyncio.run() raises RuntimeError when a loop is already running +# (simulates Jupyter notebook / FastAPI endpoint / pytest-asyncio) +# --------------------------------------------------------------------------- + +print("=" * 60) +print("PART 2: asyncio.run() inside a running event loop") +print(" (simulates Jupyter / FastAPI / pytest-asyncio)") +print("=" * 60) + +async def simulate_jupyter_cell(): + """ + Jupyter runs all cells inside a single persistent event loop. + Any code in a cell is already inside that running loop. + A sync user who tries asyncio.run() here hits RuntimeError. + """ + print("Inside running event loop (simulating a Jupyter cell)...") + + # This is what a sync user would try: + try: + result = asyncio.run(sdk_create_record("account", {"name": "Fabrikam"})) + print(f" [unexpected success] {result}") + except RuntimeError as e: + print(f" RuntimeError raised: {e}") + + print() + print(" The only way to call the SDK here is with await:") + result = await sdk_create_record("account", {"name": "Fabrikam"}) + print(f" await result: {result}") + print() + print(" So even 'sync' users in Jupyter must learn async/await.") + +asyncio.run(simulate_jupyter_cell()) diff --git a/sql_examples_output.txt b/sql_examples_output.txt new file mode 100644 index 00000000..bff882d6 --- /dev/null +++ b/sql_examples_output.txt @@ -0,0 +1,7 @@ +================================================================================ +Dataverse SDK -- SQL End-to-End (Pure SQL Workflows) +================================================================================ + +================================================================================ +1. Setup & Authentication +================================================================================ diff --git a/src/PowerPlatform/Dataverse/aio/data/_async_odata.py b/src/PowerPlatform/Dataverse/aio/data/_async_odata.py index fa3ea0dc..33b29a48 100644 --- a/src/PowerPlatform/Dataverse/aio/data/_async_odata.py +++ b/src/PowerPlatform/Dataverse/aio/data/_async_odata.py @@ -845,18 +845,22 @@ async def _create_entity( attributes: List[Dict[str, Any]], solution_unique_name: Optional[str] = None, ) -> Dict[str, Any]: - url = f"{self.api}/EntityDefinitions" + url = f"{self.api}/CreateEntities" payload = { - "@odata.type": "Microsoft.Dynamics.CRM.EntityMetadata", - "SchemaName": table_schema_name, - "DisplayName": self._label(display_name), - "DisplayCollectionName": self._label(display_name + "s"), - "Description": self._label(f"Custom entity for {display_name}"), - "OwnershipType": "UserOwned", - "HasActivities": False, - "HasNotes": True, - "IsActivity": False, - "Attributes": attributes, + "Entities": [ + { + "@odata.type": "Microsoft.Dynamics.CRM.ComplexEntityMetadata", + "SchemaName": table_schema_name, + "DisplayName": self._label(display_name), + "DisplayCollectionName": self._label(display_name + "s"), + "Description": self._label(f"Custom entity for {display_name}"), + "OwnershipType": "UserOwned", + "HasActivities": False, + "HasNotes": True, + "IsActivity": False, + "Attributes": attributes, + } + ] } params = None if solution_unique_name: @@ -1339,9 +1343,9 @@ async def _create_table( ) attributes: List[Dict[str, Any]] = [] - attributes.append(self._attribute_payload(primary_attr_schema, "string", is_primary_name=True)) + attributes.append(self._attribute_payload(primary_attr_schema, "string", is_primary_name=True, complex=True)) for col_name, dtype in schema.items(): - payload = self._attribute_payload(col_name, dtype) + payload = self._attribute_payload(col_name, dtype, complex=True) if not payload: raise ValueError(f"Unsupported column type '{dtype}' for '{col_name}'.") attributes.append(payload) From 2c1265885c29005a81e80de4642572fe49fd3d87 Mon Sep 17 00:00:00 2001 From: Abel Milash Date: Sun, 24 May 2026 14:49:56 -0700 Subject: [PATCH 31/34] Revert "Mirror PR #183 (CreateEntities API) in async client" This reverts commit c1f12374e0b31e14552c29b61f349e40d9f5caca. --- .claude/skills/dataverse-sdk-release/SKILL.md | 163 - .../async_jupyter_demo-checkpoint.ipynb | 104 - _check_exports.py | 51 - _check_imports.py | 44 - _tee_run.py | 41 - async_jupyter_demo.ipynb | 120 - coverage.json | 1 - coverage.xml | 3181 ----------------- coverage_aio.json | 1 - docs/async-design-options.md | 288 -- docs/async-design.md | 276 -- docs/design-evaluation.md | 344 -- docs/proposal-typed-model-alignment.md | 277 -- docs_local/_build/.buildinfo | 4 - .../Dataverse/claude_skill/index.doctree | Bin 3639 -> 0 bytes .../Dataverse/client/index.doctree | Bin 43856 -> 0 bytes .../Dataverse/common/constants/index.doctree | Bin 30710 -> 0 bytes .../Dataverse/common/index.doctree | Bin 3972 -> 0 bytes .../Dataverse/core/config/index.doctree | Bin 38957 -> 0 bytes .../Dataverse/core/errors/index.doctree | Bin 103492 -> 0 bytes .../Dataverse/core/index.doctree | Bin 4171 -> 0 bytes .../Dataverse/core/log_config/index.doctree | Bin 27189 -> 0 bytes .../Dataverse/data/index.doctree | Bin 3445 -> 0 bytes .../Dataverse/extensions/index.doctree | Bin 3175 -> 0 bytes .../PowerPlatform/Dataverse/index.doctree | Bin 3944 -> 0 bytes .../Dataverse/migration/index.doctree | Bin 3559 -> 0 bytes .../migration/migrate_v0_to_v1/index.doctree | Bin 42581 -> 0 bytes .../Dataverse/models/batch/index.doctree | Bin 44755 -> 0 bytes .../models/fetchxml_query/index.doctree | Bin 24175 -> 0 bytes .../Dataverse/models/filters/index.doctree | Bin 146068 -> 0 bytes .../Dataverse/models/index.doctree | Bin 10245 -> 0 bytes .../Dataverse/models/labels/index.doctree | Bin 36905 -> 0 bytes .../Dataverse/models/protocol/index.doctree | Bin 18057 -> 0 bytes .../models/query_builder/index.doctree | Bin 85179 -> 0 bytes .../Dataverse/models/record/index.doctree | Bin 53127 -> 0 bytes .../models/relationship/index.doctree | Bin 161553 -> 0 bytes .../Dataverse/models/table_info/index.doctree | Bin 106688 -> 0 bytes .../Dataverse/models/upsert/index.doctree | Bin 16866 -> 0 bytes .../Dataverse/operations/batch/index.doctree | Bin 324887 -> 0 bytes .../operations/dataframe/index.doctree | Bin 90271 -> 0 bytes .../Dataverse/operations/files/index.doctree | Bin 30164 -> 0 bytes .../Dataverse/operations/index.doctree | Bin 4419 -> 0 bytes .../Dataverse/operations/query/index.doctree | Bin 86358 -> 0 bytes .../operations/records/index.doctree | Bin 201405 -> 0 bytes .../Dataverse/operations/tables/index.doctree | Bin 213594 -> 0 bytes .../Dataverse/utils/index.doctree | Bin 3280 -> 0 bytes .../autoapi/PowerPlatform/index.doctree | Bin 3372 -> 0 bytes .../_build/.doctrees/autoapi/index.doctree | Bin 4199 -> 0 bytes .../_build/.doctrees/environment.pickle | Bin 2482769 -> 0 bytes docs_local/_build/.doctrees/index.doctree | Bin 2971 -> 0 bytes .../Dataverse/claude_skill/index.rst.txt | 15 - .../Dataverse/client/index.rst.txt | 157 - .../Dataverse/common/constants/index.rst.txt | 77 - .../Dataverse/common/index.rst.txt | 22 - .../Dataverse/core/config/index.rst.txt | 117 - .../Dataverse/core/errors/index.rst.txt | 185 - .../Dataverse/core/index.rst.txt | 25 - .../Dataverse/core/log_config/index.rst.txt | 90 - .../Dataverse/data/index.rst.txt | 14 - .../Dataverse/extensions/index.rst.txt | 11 - .../PowerPlatform/Dataverse/index.rst.txt | 24 - .../Dataverse/migration/index.rst.txt | 15 - .../migration/migrate_v0_to_v1/index.rst.txt | 119 - .../Dataverse/models/batch/index.rst.txt | 154 - .../models/fetchxml_query/index.rst.txt | 76 - .../Dataverse/models/filters/index.rst.txt | 365 -- .../Dataverse/models/index.rst.txt | 41 - .../Dataverse/models/labels/index.rst.txt | 113 - .../Dataverse/models/protocol/index.rst.txt | 94 - .../models/query_builder/index.rst.txt | 332 -- .../Dataverse/models/record/index.rst.txt | 152 - .../models/relationship/index.rst.txt | 471 --- .../Dataverse/models/table_info/index.rst.txt | 305 -- .../Dataverse/models/upsert/index.rst.txt | 54 - .../Dataverse/operations/batch/index.rst.txt | 741 ---- .../operations/dataframe/index.rst.txt | 279 -- .../Dataverse/operations/files/index.rst.txt | 99 - .../Dataverse/operations/index.rst.txt | 28 - .../Dataverse/operations/query/index.rst.txt | 345 -- .../operations/records/index.rst.txt | 461 --- .../Dataverse/operations/tables/index.rst.txt | 683 ---- .../Dataverse/utils/index.rst.txt | 13 - .../autoapi/PowerPlatform/index.rst.txt | 15 - .../_build/_sources/autoapi/index.rst.txt | 11 - docs_local/_build/_sources/index.rst.txt | 8 - docs_local/_build/_static/alabaster.css | 708 ---- docs_local/_build/_static/base-stemmer.js | 476 --- docs_local/_build/_static/basic.css | 906 ----- docs_local/_build/_static/custom.css | 1 - docs_local/_build/_static/doctools.js | 150 - .../_build/_static/documentation_options.js | 13 - docs_local/_build/_static/english-stemmer.js | 1066 ------ docs_local/_build/_static/file.png | Bin 286 -> 0 bytes docs_local/_build/_static/graphviz.css | 12 - docs_local/_build/_static/language_data.js | 13 - docs_local/_build/_static/minus.png | Bin 90 -> 0 bytes docs_local/_build/_static/plus.png | Bin 90 -> 0 bytes docs_local/_build/_static/pygments.css | 84 - docs_local/_build/_static/searchtools.js | 693 ---- docs_local/_build/_static/sphinx_highlight.js | 159 - .../Dataverse/claude_skill/index.html | 122 - .../PowerPlatform/Dataverse/client/index.html | 295 -- .../Dataverse/common/constants/index.html | 209 -- .../PowerPlatform/Dataverse/common/index.html | 128 - .../Dataverse/core/config/index.html | 231 -- .../Dataverse/core/errors/index.html | 304 -- .../PowerPlatform/Dataverse/core/index.html | 131 - .../Dataverse/core/log_config/index.html | 201 -- .../PowerPlatform/Dataverse/data/index.html | 121 - .../Dataverse/extensions/index.html | 119 - .../PowerPlatform/Dataverse/index.html | 133 - .../Dataverse/migration/index.html | 126 - .../migration/migrate_v0_to_v1/index.html | 247 -- .../Dataverse/models/batch/index.html | 267 -- .../models/fetchxml_query/index.html | 201 -- .../Dataverse/models/filters/index.html | 550 --- .../PowerPlatform/Dataverse/models/index.html | 148 - .../Dataverse/models/labels/index.html | 233 -- .../Dataverse/models/protocol/index.html | 202 -- .../Dataverse/models/query_builder/index.html | 504 --- .../Dataverse/models/record/index.html | 273 -- .../Dataverse/models/relationship/index.html | 586 --- .../Dataverse/models/table_info/index.html | 403 --- .../Dataverse/models/upsert/index.html | 171 - .../Dataverse/operations/batch/index.html | 932 ----- .../Dataverse/operations/dataframe/index.html | 413 --- .../Dataverse/operations/files/index.html | 212 -- .../Dataverse/operations/index.html | 134 - .../Dataverse/operations/query/index.html | 497 --- .../Dataverse/operations/records/index.html | 588 --- .../Dataverse/operations/tables/index.html | 865 ----- .../PowerPlatform/Dataverse/utils/index.html | 118 - .../_build/autoapi/PowerPlatform/index.html | 122 - docs_local/_build/autoapi/index.html | 170 - docs_local/_build/genindex.html | 1331 ------- docs_local/_build/index.html | 116 - docs_local/_build/objects.inv | Bin 3506 -> 0 bytes docs_local/_build/py-modindex.html | 279 -- docs_local/_build/search.html | 121 - docs_local/_build/searchindex.js | 1 - .../Dataverse/claude_skill/index.rst | 15 - .../PowerPlatform/Dataverse/client/index.rst | 157 - .../Dataverse/common/constants/index.rst | 77 - .../PowerPlatform/Dataverse/common/index.rst | 22 - .../Dataverse/core/config/index.rst | 117 - .../Dataverse/core/errors/index.rst | 185 - .../PowerPlatform/Dataverse/core/index.rst | 25 - .../Dataverse/core/log_config/index.rst | 90 - .../PowerPlatform/Dataverse/data/index.rst | 14 - .../Dataverse/extensions/index.rst | 11 - .../autoapi/PowerPlatform/Dataverse/index.rst | 24 - .../Dataverse/migration/index.rst | 15 - .../migration/migrate_v0_to_v1/index.rst | 119 - .../Dataverse/models/batch/index.rst | 154 - .../Dataverse/models/fetchxml_query/index.rst | 76 - .../Dataverse/models/filters/index.rst | 365 -- .../PowerPlatform/Dataverse/models/index.rst | 41 - .../Dataverse/models/labels/index.rst | 113 - .../Dataverse/models/protocol/index.rst | 94 - .../Dataverse/models/query_builder/index.rst | 332 -- .../Dataverse/models/record/index.rst | 152 - .../Dataverse/models/relationship/index.rst | 471 --- .../Dataverse/models/table_info/index.rst | 305 -- .../Dataverse/models/upsert/index.rst | 54 - .../Dataverse/operations/batch/index.rst | 741 ---- .../Dataverse/operations/dataframe/index.rst | 279 -- .../Dataverse/operations/files/index.rst | 99 - .../Dataverse/operations/index.rst | 28 - .../Dataverse/operations/query/index.rst | 345 -- .../Dataverse/operations/records/index.rst | 461 --- .../Dataverse/operations/tables/index.rst | 683 ---- .../PowerPlatform/Dataverse/utils/index.rst | 13 - docs_local/autoapi/PowerPlatform/index.rst | 15 - docs_local/autoapi/index.rst | 11 - docs_local/build.log | 216 -- docs_local/build_after_fixes.log | 125 - docs_local/build_final.log | 130 - docs_local/build_no_imported.log | 120 - docs_local/build_option1.log | 140 - docs_local/build_optionA.log | 200 -- docs_local/build_post_merge.log | 134 - docs_local/build_post_restore.log | 187 - docs_local/build_review.log | 120 - docs_local/conf.py | 41 - docs_local/index.rst | 8 - examples/advanced/memo_walkthrough.py | 426 --- examples/advanced/perf_benchmark_live.py | 255 -- examples/advanced/perf_benchmark_mock.py | 309 -- examples/advanced/picklist_walkthrough.py | 440 --- examples/advanced/test_querybuilder_live.py | 955 ----- forge.txt | 557 --- functional_testing_log.txt | 191 - functional_testing_output.txt | 24 - logs/log.txt | 245 -- logs/package_logs/None.txt | 4 - .../PowerPlatform-Dataverse-Client.txt | 60 - scratch_async_demo.py | 82 - sql_examples_output.txt | 7 - .../Dataverse/aio/data/_async_odata.py | 30 +- 199 files changed, 13 insertions(+), 37387 deletions(-) delete mode 100644 .claude/skills/dataverse-sdk-release/SKILL.md delete mode 100644 .ipynb_checkpoints/async_jupyter_demo-checkpoint.ipynb delete mode 100644 _check_exports.py delete mode 100644 _check_imports.py delete mode 100644 _tee_run.py delete mode 100644 async_jupyter_demo.ipynb delete mode 100644 coverage.json delete mode 100644 coverage.xml delete mode 100644 coverage_aio.json delete mode 100644 docs/async-design-options.md delete mode 100644 docs/async-design.md delete mode 100644 docs/design-evaluation.md delete mode 100644 docs/proposal-typed-model-alignment.md delete mode 100644 docs_local/_build/.buildinfo delete mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/claude_skill/index.doctree delete mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/client/index.doctree delete mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/common/constants/index.doctree delete mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/common/index.doctree delete mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/core/config/index.doctree delete mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/core/errors/index.doctree delete mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/core/index.doctree delete mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/core/log_config/index.doctree delete mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/data/index.doctree delete mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/extensions/index.doctree delete mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/index.doctree delete mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/migration/index.doctree delete mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index.doctree delete mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/batch/index.doctree delete mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index.doctree delete mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/filters/index.doctree delete mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/index.doctree delete mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/labels/index.doctree delete mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/protocol/index.doctree delete mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/query_builder/index.doctree delete mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/record/index.doctree delete mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/relationship/index.doctree delete mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/table_info/index.doctree delete mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/upsert/index.doctree delete mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/operations/batch/index.doctree delete mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/operations/dataframe/index.doctree delete mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/operations/files/index.doctree delete mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/operations/index.doctree delete mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/operations/query/index.doctree delete mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/operations/records/index.doctree delete mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/operations/tables/index.doctree delete mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/utils/index.doctree delete mode 100644 docs_local/_build/.doctrees/autoapi/PowerPlatform/index.doctree delete mode 100644 docs_local/_build/.doctrees/autoapi/index.doctree delete mode 100644 docs_local/_build/.doctrees/environment.pickle delete mode 100644 docs_local/_build/.doctrees/index.doctree delete mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/claude_skill/index.rst.txt delete mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/client/index.rst.txt delete mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/common/constants/index.rst.txt delete mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/common/index.rst.txt delete mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/core/config/index.rst.txt delete mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/core/errors/index.rst.txt delete mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/core/index.rst.txt delete mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/core/log_config/index.rst.txt delete mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/data/index.rst.txt delete mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/extensions/index.rst.txt delete mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/index.rst.txt delete mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/migration/index.rst.txt delete mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index.rst.txt delete mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/batch/index.rst.txt delete mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index.rst.txt delete mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/filters/index.rst.txt delete mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/index.rst.txt delete mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/labels/index.rst.txt delete mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/protocol/index.rst.txt delete mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/query_builder/index.rst.txt delete mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/record/index.rst.txt delete mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/relationship/index.rst.txt delete mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/table_info/index.rst.txt delete mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/upsert/index.rst.txt delete mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/batch/index.rst.txt delete mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/dataframe/index.rst.txt delete mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/files/index.rst.txt delete mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/index.rst.txt delete mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/query/index.rst.txt delete mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/records/index.rst.txt delete mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/tables/index.rst.txt delete mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/utils/index.rst.txt delete mode 100644 docs_local/_build/_sources/autoapi/PowerPlatform/index.rst.txt delete mode 100644 docs_local/_build/_sources/autoapi/index.rst.txt delete mode 100644 docs_local/_build/_sources/index.rst.txt delete mode 100644 docs_local/_build/_static/alabaster.css delete mode 100644 docs_local/_build/_static/base-stemmer.js delete mode 100644 docs_local/_build/_static/basic.css delete mode 100644 docs_local/_build/_static/custom.css delete mode 100644 docs_local/_build/_static/doctools.js delete mode 100644 docs_local/_build/_static/documentation_options.js delete mode 100644 docs_local/_build/_static/english-stemmer.js delete mode 100644 docs_local/_build/_static/file.png delete mode 100644 docs_local/_build/_static/graphviz.css delete mode 100644 docs_local/_build/_static/language_data.js delete mode 100644 docs_local/_build/_static/minus.png delete mode 100644 docs_local/_build/_static/plus.png delete mode 100644 docs_local/_build/_static/pygments.css delete mode 100644 docs_local/_build/_static/searchtools.js delete mode 100644 docs_local/_build/_static/sphinx_highlight.js delete mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/claude_skill/index.html delete mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/client/index.html delete mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/common/constants/index.html delete mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/common/index.html delete mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/core/config/index.html delete mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/core/errors/index.html delete mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/core/index.html delete mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/core/log_config/index.html delete mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/data/index.html delete mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/extensions/index.html delete mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/index.html delete mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/migration/index.html delete mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index.html delete mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/models/batch/index.html delete mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index.html delete mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/models/filters/index.html delete mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/models/index.html delete mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/models/labels/index.html delete mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/models/protocol/index.html delete mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/models/query_builder/index.html delete mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/models/record/index.html delete mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/models/relationship/index.html delete mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/models/table_info/index.html delete mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/models/upsert/index.html delete mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/batch/index.html delete mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/dataframe/index.html delete mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/files/index.html delete mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/index.html delete mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/query/index.html delete mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/records/index.html delete mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/tables/index.html delete mode 100644 docs_local/_build/autoapi/PowerPlatform/Dataverse/utils/index.html delete mode 100644 docs_local/_build/autoapi/PowerPlatform/index.html delete mode 100644 docs_local/_build/autoapi/index.html delete mode 100644 docs_local/_build/genindex.html delete mode 100644 docs_local/_build/index.html delete mode 100644 docs_local/_build/objects.inv delete mode 100644 docs_local/_build/py-modindex.html delete mode 100644 docs_local/_build/search.html delete mode 100644 docs_local/_build/searchindex.js delete mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/claude_skill/index.rst delete mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/client/index.rst delete mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/common/constants/index.rst delete mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/common/index.rst delete mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/core/config/index.rst delete mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/core/errors/index.rst delete mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/core/index.rst delete mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/core/log_config/index.rst delete mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/data/index.rst delete mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/extensions/index.rst delete mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/index.rst delete mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/migration/index.rst delete mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index.rst delete mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/models/batch/index.rst delete mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index.rst delete mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/models/filters/index.rst delete mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/models/index.rst delete mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/models/labels/index.rst delete mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/models/protocol/index.rst delete mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/models/query_builder/index.rst delete mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/models/record/index.rst delete mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/models/relationship/index.rst delete mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/models/table_info/index.rst delete mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/models/upsert/index.rst delete mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/operations/batch/index.rst delete mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/operations/dataframe/index.rst delete mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/operations/files/index.rst delete mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/operations/index.rst delete mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/operations/query/index.rst delete mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/operations/records/index.rst delete mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/operations/tables/index.rst delete mode 100644 docs_local/autoapi/PowerPlatform/Dataverse/utils/index.rst delete mode 100644 docs_local/autoapi/PowerPlatform/index.rst delete mode 100644 docs_local/autoapi/index.rst delete mode 100644 docs_local/build.log delete mode 100644 docs_local/build_after_fixes.log delete mode 100644 docs_local/build_final.log delete mode 100644 docs_local/build_no_imported.log delete mode 100644 docs_local/build_option1.log delete mode 100644 docs_local/build_optionA.log delete mode 100644 docs_local/build_post_merge.log delete mode 100644 docs_local/build_post_restore.log delete mode 100644 docs_local/build_review.log delete mode 100644 docs_local/conf.py delete mode 100644 docs_local/index.rst delete mode 100644 examples/advanced/memo_walkthrough.py delete mode 100644 examples/advanced/perf_benchmark_live.py delete mode 100644 examples/advanced/perf_benchmark_mock.py delete mode 100644 examples/advanced/picklist_walkthrough.py delete mode 100644 examples/advanced/test_querybuilder_live.py delete mode 100644 forge.txt delete mode 100644 functional_testing_log.txt delete mode 100644 functional_testing_output.txt delete mode 100644 logs/log.txt delete mode 100644 logs/package_logs/None.txt delete mode 100644 logs/package_logs/PowerPlatform-Dataverse-Client.txt delete mode 100644 scratch_async_demo.py delete mode 100644 sql_examples_output.txt diff --git a/.claude/skills/dataverse-sdk-release/SKILL.md b/.claude/skills/dataverse-sdk-release/SKILL.md deleted file mode 100644 index dd99d21f..00000000 --- a/.claude/skills/dataverse-sdk-release/SKILL.md +++ /dev/null @@ -1,163 +0,0 @@ -# Dataverse Python SDK Release Guide - -Step-by-step release process for the PowerPlatform Dataverse Client Python SDK. - -**SDK Repository**: [microsoft/PowerPlatform-DataverseClient-Python](https://github.com/microsoft/PowerPlatform-DataverseClient-Python) -**PyPI Package**: [PowerPlatform-Dataverse-Client](https://pypi.org/project/PowerPlatform-Dataverse-Client/) - ---- - -## Prerequisites - -- Write access to the GitHub repository (microsoft/PowerPlatform-DataverseClient-Python) -- Access to the Azure DevOps CI/CD pipeline for PyPI publishing: https://dev.azure.com/dynamicscrm/OneCRM/_build?definitionId=29949 -- _(Optional)_ **GitHub CLI (`gh`)** — enables automated PR creation and GitHub releases from the terminal. - - Install: `winget install GitHub.cli`, then `gh auth login`. - ---- - -## Release Checklist - -### Step 1: Identify Changes Since Last Release - -1. Find the last release version and date in CHANGELOG.md (the current dev version is in `pyproject.toml` under `version`) -2. List merged PRs since the last release: - - GitHub UI: https://github.com/microsoft/PowerPlatform-DataverseClient-Python/pulls?q=is%3Apr+is%3Amerged - - Or via git: `git log --oneline --since=""` -3. For each PR, read the full description to understand the user-facing impact - -### Step 2: Update CHANGELOG.md - -1. Create a branch: `git checkout -b release/v` (e.g., `release/v0.1.0b6`) -2. Add a new section at the top of CHANGELOG.md (below the header), using today's date: - -```markdown -## [X.Y.Z] - YYYY-MM-DD - -### Added -- Description of new feature (#PR_NUMBER) - -### Fixed -- Description of bug fix (#PR_NUMBER) -``` - -3. Add a version comparison link at the bottom of CHANGELOG.md: - -```markdown -[X.Y.Z]: https://github.com/microsoft/PowerPlatform-DataverseClient-Python/compare/vPREVIOUS...vX.Y.Z -``` - -**Changelog writing rules:** - -- Focus on **why it matters to users**, not implementation details -- Do NOT reference internal function names or implementation choices -- DO describe the user-visible behavior change or new capability -- Include PR numbers for reference: `(#123)` - -**Category headings:** -- New features → **Added** -- Changes to existing functionality → **Changed** -- Soon-to-be removed features → **Deprecated** -- Removed features → **Removed** -- Bug fixes → **Fixed** -- Security fixes → **Security** - -**What to exclude:** -- Internal refactoring (unless it affects performance/behavior) -- Test-only changes -- CI/CD changes -- Documentation-only updates - -### Step 3: Create PR for Changelog - -1. Commit: `git add CHANGELOG.md && git commit -m "Update CHANGELOG.md for v release"` -2. Push: `git push -u origin release/v` -3. Create a PR on GitHub targeting `main`: - - **With `gh` CLI:** `gh pr create --base main --title "Update CHANGELOG.md for v release" --body "Release changelog for v"` -4. Get the PR reviewed and merged - -### Step 4: Create Git Tag - -After the changelog PR is merged: - -1. Pull latest main: -```bash -git switch main -git pull origin main -``` - -2. Create and push the tag: -```bash -git tag -a v -m "Release v" -git push origin v -``` - -> **Important:** The tag must be on the `main` commit that includes the changelog update. - -### Step 5: Publish to PyPI - -Trigger the Azure DevOps CI/CD pipeline: -- Pipeline: https://dev.azure.com/dynamicscrm/OneCRM/_build?definitionId=29949 - -**Runtime variables (set when queuing the pipeline):** - -| Variable | Description | -|---|---| -| `PushToPyPI` | Set to `true` to publish to PyPI. If not set, the pipeline builds, tests, and produces artifacts without publishing. | -| `PackageVersion` | The version string for the release (e.g., `0.1.0b7`). Leave empty to use the version from `pyproject.toml`. | - -**Recommendation:** First run the pipeline **without** setting `PushToPyPI` to `true`. This validates the build, tests, and packaging. Once the dry run succeeds, queue again with `PushToPyPI` set to `true` to publish. - -Verify the package appears on PyPI: https://pypi.org/project/PowerPlatform-Dataverse-Client/ - -### Step 6: Create GitHub Release - -**Before writing release notes:** Review previous releases at https://github.com/microsoft/PowerPlatform-DataverseClient-Python/releases to match the tone, detail level, and formatting conventions. - -- **With `gh` CLI:** Extract the release notes from CHANGELOG.md (the Added/Fixed/Changed sections for this version) into a temp file, then run: - ```bash - gh release create v --title "v" --notes-file --prerelease - ``` - Omit `--prerelease` if the version does **not** contain `a`, `b`, or `rc`. -- **Without `gh` CLI:** - 1. Go to: https://github.com/microsoft/PowerPlatform-DataverseClient-Python/releases/new - 2. Select the tag: `v` - 3. Title: `v` - 4. Copy release notes from CHANGELOG.md - 5. Check **"Set as a pre-release"** if the version contains `a`, `b`, or `rc` (alpha/beta/release candidate) - 6. Click **Publish release** - -### Step 7: Post-Release Version Bump - -Immediately after the release, bump the version for the next development cycle: - -1. Create a branch: `git checkout -b post-release/bump-` -2. Update `version` in `pyproject.toml` to the next beta (e.g., `0.1.0b6` → `0.1.0b7`) -3. Stage and commit: -```bash -git add pyproject.toml -git commit -m "Bump version to for next development cycle" -``` -4. Push: `git push -u origin post-release/bump-` -5. Create a PR on GitHub and merge it: - - **With `gh` CLI:** `gh pr create --base main --title "Bump version to for next development cycle" --body "Post-release version bump"` - ---- - -## Version Numbering - -This project uses Semantic Versioning with PEP 440 pre-release identifiers: -- Beta releases: `0.1.0b1`, `0.1.0b2`, `0.1.0b3`, ... -- The version in `pyproject.toml` on `main` should always be one ahead of the latest published release - ---- - -## Key Links - -| Resource | URL | -|---|---| -| Repository | https://github.com/microsoft/PowerPlatform-DataverseClient-Python | -| PyPI Package | https://pypi.org/project/PowerPlatform-Dataverse-Client/ | -| CI/CD Pipeline | https://dev.azure.com/dynamicscrm/OneCRM/_build?definitionId=29949 | -| Releases | https://github.com/microsoft/PowerPlatform-DataverseClient-Python/releases | -| Contributing Guide | https://github.com/microsoft/PowerPlatform-DataverseClient-Python/blob/main/CONTRIBUTING.md | \ No newline at end of file diff --git a/.ipynb_checkpoints/async_jupyter_demo-checkpoint.ipynb b/.ipynb_checkpoints/async_jupyter_demo-checkpoint.ipynb deleted file mode 100644 index b9f83332..00000000 --- a/.ipynb_checkpoints/async_jupyter_demo-checkpoint.ipynb +++ /dev/null @@ -1,104 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Async-only SDK in Jupyter — what sync users experience\n", - "\n", - "Jupyter runs all cells inside a **persistent event loop**. This notebook shows what happens when a sync user tries to use an async-only SDK." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import asyncio\n", - "\n", - "# Simulated async-only SDK method (no sync client exists)\n", - "async def sdk_create_record(table: str, data: dict) -> str:\n", - " await asyncio.sleep(0) # yields control, like a real network call\n", - " return f\"fake-guid-for-{data.get('name', 'unknown')}\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Attempt 1 — call without `await` (forgetting to await)\n", - "\n", - "A user unfamiliar with async might call the function directly." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "result = sdk_create_record(\"account\", {\"name\": \"Contoso\"})\n", - "print(f\"Return value: {result}\")\n", - "print(f\"Type: {type(result)}\")\n", - "print()\n", - "print(\"The coroutine was never executed — no network call happened.\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Attempt 2 — use `asyncio.run()` (the \"sync\" workaround)\n", - "\n", - "A sync user who learns about `asyncio.run()` tries to use it to avoid writing `async def`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# This is what a sync user would try after reading the docs\n", - "record_id = asyncio.run(sdk_create_record(\"account\", {\"name\": \"Contoso\"}))\n", - "print(f\"Created: {record_id}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Attempt 3 — use `await` (correct, but requires `async def`)\n", - "\n", - "The only way to call the SDK in Jupyter is with `await` — which works because Jupyter supports top-level `await`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# This works — Jupyter supports top-level await\n", - "record_id = await sdk_create_record(\"account\", {\"name\": \"Contoso\"})\n", - "print(f\"Created: {record_id}\")\n", - "print()\n", - "print(\"Works — but the user must learn async/await just to use the SDK.\")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "name": "python", - "version": "3.12.0" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/_check_exports.py b/_check_exports.py deleted file mode 100644 index 0b2ffb28..00000000 --- a/_check_exports.py +++ /dev/null @@ -1,51 +0,0 @@ -"""Temporary script to compare module __all__ vs package __init__.py re-exports.""" -import importlib - -packages = { - "models": [ - "PowerPlatform.Dataverse.models.batch", - "PowerPlatform.Dataverse.models.fetchxml_query", - "PowerPlatform.Dataverse.models.filters", - "PowerPlatform.Dataverse.models.labels", - "PowerPlatform.Dataverse.models.protocol", - "PowerPlatform.Dataverse.models.query_builder", - "PowerPlatform.Dataverse.models.record", - "PowerPlatform.Dataverse.models.relationship", - "PowerPlatform.Dataverse.models.table_info", - "PowerPlatform.Dataverse.models.upsert", - ], - "core": [ - "PowerPlatform.Dataverse.core.config", - "PowerPlatform.Dataverse.core.errors", - "PowerPlatform.Dataverse.core.log_config", - ], - "operations": [ - "PowerPlatform.Dataverse.operations.batch", - "PowerPlatform.Dataverse.operations.dataframe", - "PowerPlatform.Dataverse.operations.files", - "PowerPlatform.Dataverse.operations.query", - "PowerPlatform.Dataverse.operations.records", - "PowerPlatform.Dataverse.operations.tables", - ], -} - -for pkg_name, modules in packages.items(): - pkg = importlib.import_module("PowerPlatform.Dataverse." + pkg_name) - pkg_all = set(pkg.__all__) - all_module_exports = set() - - for mod_name in modules: - mod = importlib.import_module(mod_name) - mod_all = set(getattr(mod, "__all__", [])) - all_module_exports |= mod_all - missing = mod_all - pkg_all - if missing: - short = mod_name.split(".")[-1] - print("MISSING from " + pkg_name + "/__init__.py (in " + short + "): " + str(sorted(missing))) - - extra = pkg_all - all_module_exports - if extra: - print("EXTRA in " + pkg_name + "/__init__.py: " + str(sorted(extra))) - - if not (all_module_exports - pkg_all) and not extra: - print(pkg_name + ": All exports match perfectly") diff --git a/_check_imports.py b/_check_imports.py deleted file mode 100644 index 76a5d10c..00000000 --- a/_check_imports.py +++ /dev/null @@ -1,44 +0,0 @@ -"""Check that all example scripts can import their dependencies.""" -import ast -import importlib -import sys -import os - -EXAMPLES = [ - "examples/basic/installation_example.py", - "examples/basic/functional_testing.py", - "examples/advanced/walkthrough.py", - "examples/advanced/sql_examples.py", - "examples/advanced/dataframe_operations.py", - "examples/advanced/batch.py", - "examples/advanced/relationships.py", - "examples/advanced/fetchxml.py", - "examples/advanced/alternate_keys_upsert.py", - "examples/advanced/file_upload.py", - "examples/advanced/prodev_quick_start.py", - "examples/advanced/datascience_risk_assessment.py", -] - -failures = [] -for path in EXAMPLES: - name = os.path.basename(path) - try: - with open(path) as f: - tree = ast.parse(f.read()) - for node in ast.walk(tree): - if isinstance(node, ast.ImportFrom) and node.module and node.level == 0: - importlib.import_module(node.module) - elif isinstance(node, ast.Import): - for alias in node.names: - importlib.import_module(alias.name) - print(f"[OK] {name}") - except Exception as e: - print(f"[ERR] {name}: {e}") - failures.append(name) - -print() -if failures: - print(f"FAILURES: {failures}") - sys.exit(1) -else: - print("All 12 examples import successfully.") diff --git a/_tee_run.py b/_tee_run.py deleted file mode 100644 index 28dfa340..00000000 --- a/_tee_run.py +++ /dev/null @@ -1,41 +0,0 @@ -"""Wrapper that tees stdout/stderr to a log file while preserving interactive TTY.""" -import sys -import os -import io - - -class _Tee: - def __init__(self, original, log_file): - self._original = original - self._log = log_file - - def write(self, data): - self._original.write(data) - self._original.flush() - self._log.write(data) - self._log.flush() - - def flush(self): - self._original.flush() - self._log.flush() - - def __getattr__(self, name): - return getattr(self._original, name) - - -if __name__ == "__main__": - if len(sys.argv) < 3: - print(f"Usage: {sys.argv[0]} [args...]") - sys.exit(1) - - log_path = sys.argv[1] - script = sys.argv[2] - sys.argv = sys.argv[2:] # make the target script see its own argv - - with open(log_path, "w", encoding="utf-8") as log_file: - sys.stdout = _Tee(sys.__stdout__, log_file) - sys.stderr = _Tee(sys.__stderr__, log_file) - - with open(script) as f: - code = compile(f.read(), script, "exec") - exec(code, {"__name__": "__main__", "__file__": script}) diff --git a/async_jupyter_demo.ipynb b/async_jupyter_demo.ipynb deleted file mode 100644 index 1a3f9f8b..00000000 --- a/async_jupyter_demo.ipynb +++ /dev/null @@ -1,120 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "bae4ba8a", - "metadata": {}, - "source": [ - "## Async-only SDK in Jupyter — what sync users experience\n", - "\n", - "Jupyter runs all cells inside a **persistent event loop**. This notebook shows what happens when a sync user tries to use an async-only SDK." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ca683481", - "metadata": {}, - "outputs": [], - "source": [ - "import asyncio\n", - "\n", - "# Simulated async-only SDK method (no sync client exists)\n", - "async def sdk_create_record(table: str, data: dict) -> str:\n", - " await asyncio.sleep(0) # yields control, like a real network call\n", - " return f\"fake-guid-for-{data.get('name', 'unknown')}\"" - ] - }, - { - "cell_type": "markdown", - "id": "44b76928", - "metadata": {}, - "source": [ - "### Attempt 1 — call without `await` (forgetting to await)\n", - "\n", - "A user unfamiliar with async might call the function directly." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ef36bec7", - "metadata": {}, - "outputs": [], - "source": [ - "result = sdk_create_record(\"account\", {\"name\": \"Contoso\"})\n", - "print(f\"Return value: {result}\")\n", - "print(f\"Type: {type(result)}\")\n", - "print()\n", - "print(\"The coroutine was never executed — no network call happened.\")" - ] - }, - { - "cell_type": "markdown", - "id": "a69ac429", - "metadata": {}, - "source": [ - "### Attempt 2 — use `asyncio.run()` (the \"sync\" workaround)\n", - "\n", - "A sync user who learns about `asyncio.run()` tries to use it to avoid writing `async def`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "731964bd", - "metadata": {}, - "outputs": [], - "source": [ - "# This is what a sync user would try after reading the docs\n", - "record_id = asyncio.run(sdk_create_record(\"account\", {\"name\": \"Contoso\"}))\n", - "print(f\"Created: {record_id}\")" - ] - }, - { - "cell_type": "markdown", - "id": "ba7777dd", - "metadata": {}, - "source": [ - "### Attempt 3 — use `await` (correct, but requires `async def`)\n", - "\n", - "The only way to call the SDK in Jupyter is with `await` — which works because Jupyter supports top-level `await`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b44f7309", - "metadata": {}, - "outputs": [], - "source": [ - "# This works — Jupyter supports top-level await\n", - "record_id = await sdk_create_record(\"account\", {\"name\": \"Contoso\"})\n", - "print(f\"Created: {record_id}\")\n", - "print()\n", - "print(\"Works — but the user must learn async/await just to use the SDK.\")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.13.9" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/coverage.json b/coverage.json deleted file mode 100644 index b5edbb9b..00000000 --- a/coverage.json +++ /dev/null @@ -1 +0,0 @@ -{"meta": {"format": 3, "version": "7.13.5", "timestamp": "2026-04-27T21:59:36.005769", "branch_coverage": false, "show_contexts": false}, "files": {"src\\PowerPlatform\\Dataverse\\__init__.py": {"executed_lines": [4, 6, 8], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [4, 6, 8], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [4, 6, 8], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\_skill_installer.py": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 139, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 139, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [13, 14, 15, 18, 24, 28, 29, 31, 34, 35, 36, 40, 41, 42, 44, 47, 49, 50, 56, 66, 67, 70, 71, 73, 74, 77, 78, 79, 80, 81, 83, 84, 85, 86, 87, 88, 91, 92, 93, 94, 95, 96, 97, 98, 101, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 129, 132, 139, 141, 142, 144, 145, 146, 147, 149, 150, 151, 152, 153, 154, 155, 156, 158, 159, 160, 162, 163, 164, 167, 169, 171, 172, 174, 175, 176, 177, 179, 180, 181, 182, 183, 184, 185, 187, 188, 190, 191, 193, 194, 195, 196, 197, 198, 199, 201, 202, 205, 207, 209, 238, 242, 244, 246, 248, 249, 250, 251, 254, 255, 256, 258, 259, 260, 263, 264, 267, 268], "excluded_lines": [], "functions": {"get_skill_source_paths": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 11, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 11, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [24, 28, 29, 31, 34, 35, 36, 40, 41, 42, 44], "excluded_lines": [], "start_line": 18}, "get_skill_destination_paths": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [49, 50], "excluded_lines": [], "start_line": 47}, "install_skill": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 49, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 49, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [66, 67, 70, 71, 73, 74, 77, 78, 79, 80, 81, 83, 84, 85, 86, 87, 88, 91, 92, 93, 94, 95, 96, 97, 98, 101, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 129], "excluded_lines": [], "start_line": 56}, "uninstall_skill": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 21, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 21, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [139, 141, 142, 144, 145, 146, 147, 149, 150, 151, 152, 153, 154, 155, 156, 158, 159, 160, 162, 163, 164], "excluded_lines": [], "start_line": 132}, "check_skill_status": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 27, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 27, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [169, 171, 172, 174, 175, 176, 177, 179, 180, 181, 182, 183, 184, 185, 187, 188, 190, 191, 193, 194, 195, 196, 197, 198, 199, 201, 202], "excluded_lines": [], "start_line": 167}, "main": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 18, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 18, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [207, 209, 238, 242, 244, 246, 248, 249, 250, 251, 254, 255, 256, 258, 259, 260, 263, 264], "excluded_lines": [], "start_line": 205}, "": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 11, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 11, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [13, 14, 15, 18, 47, 56, 132, 167, 205, 267, 268], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 139, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 139, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [13, 14, 15, 18, 24, 28, 29, 31, 34, 35, 36, 40, 41, 42, 44, 47, 49, 50, 56, 66, 67, 70, 71, 73, 74, 77, 78, 79, 80, 81, 83, 84, 85, 86, 87, 88, 91, 92, 93, 94, 95, 96, 97, 98, 101, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 129, 132, 139, 141, 142, 144, 145, 146, 147, 149, 150, 151, 152, 153, 154, 155, 156, 158, 159, 160, 162, 163, 164, 167, 169, 171, 172, 174, 175, 176, 177, 179, 180, 181, 182, 183, 184, 185, 187, 188, 190, 191, 193, 194, 195, 196, 197, 198, 199, 201, 202, 205, 207, 209, 238, 242, 244, 246, 248, 249, 250, 251, 254, 255, 256, 258, 259, 260, 263, 264, 267, 268], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\__init__.py": {"executed_lines": [4, 6], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [4, 6], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [4, 6], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\async_client.py": {"executed_lines": [4, 6, 7, 9, 10, 12, 13, 14, 15, 16, 17, 18, 19, 20, 23, 93, 99, 100, 101, 102, 103, 104, 105, 106, 109, 110, 111, 112, 113, 114, 116, 126, 127, 133, 135, 136, 138, 139, 141, 142, 146, 158, 159, 160, 161, 163, 169, 171, 188, 189, 190, 191, 192, 193, 194, 195, 196, 198, 200, 201], "summary": {"covered_lines": 60, "num_statements": 60, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"AsyncDataverseClient.__init__": {"executed_lines": [99, 100, 101, 102, 103, 104, 105, 106, 109, 110, 111, 112, 113, 114], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 93}, "AsyncDataverseClient._get_odata": {"executed_lines": [126, 127, 133], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 116}, "AsyncDataverseClient._scoped_odata": {"executed_lines": [138, 139, 141, 142], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 136}, "AsyncDataverseClient.__aenter__": {"executed_lines": [158, 159, 160, 161], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 146}, "AsyncDataverseClient.__aexit__": {"executed_lines": [169], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 163}, "AsyncDataverseClient.aclose": {"executed_lines": [188, 189, 190, 191, 192, 193, 194, 195, 196], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 171}, "AsyncDataverseClient._check_closed": {"executed_lines": [200, 201], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 198}, "": {"executed_lines": [4, 6, 7, 9, 10, 12, 13, 14, 15, 16, 17, 18, 19, 20, 23, 93, 116, 135, 136, 146, 163, 171, 198], "summary": {"covered_lines": 23, "num_statements": 23, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"AsyncDataverseClient": {"executed_lines": [99, 100, 101, 102, 103, 104, 105, 106, 109, 110, 111, 112, 113, 114, 126, 127, 133, 138, 139, 141, 142, 158, 159, 160, 161, 169, 188, 189, 190, 191, 192, 193, 194, 195, 196, 200, 201], "summary": {"covered_lines": 37, "num_statements": 37, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 23}, "": {"executed_lines": [4, 6, 7, 9, 10, 12, 13, 14, 15, 16, 17, 18, 19, 20, 23, 93, 116, 135, 136, 146, 163, 171, 198], "summary": {"covered_lines": 23, "num_statements": 23, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\core\\__init__.py": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\core\\_async_auth.py": {"executed_lines": [13, 15, 17, 20, 29, 30, 31, 32, 34, 44, 45], "summary": {"covered_lines": 11, "num_statements": 11, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"_AsyncAuthManager.__init__": {"executed_lines": [30, 31, 32], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 29}, "_AsyncAuthManager._acquire_token": {"executed_lines": [44, 45], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 34}, "": {"executed_lines": [13, 15, 17, 20, 29, 34], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_AsyncAuthManager": {"executed_lines": [30, 31, 32, 44, 45], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 20}, "": {"executed_lines": [13, 15, 17, 20, 29, 34], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\core\\_async_http.py": {"executed_lines": [12, 14, 15, 16, 18, 24, 47, 55, 56, 57, 58, 59, 61, 82, 83, 87, 88, 89, 91, 92, 93, 97, 98, 100, 103, 104, 112, 113, 114, 115, 118, 119, 121, 124, 125, 133, 134, 135, 136, 143, 144, 145, 146, 147, 149, 154, 155, 156], "summary": {"covered_lines": 48, "num_statements": 50, "percent_covered": 96.0, "percent_covered_display": "96", "missing_lines": 2, "excluded_lines": 2, "percent_statements_covered": 96.0, "percent_statements_covered_display": "96"}, "missing_lines": [99, 101], "excluded_lines": [20, 21], "functions": {"_AsyncHttpClient.__init__": {"executed_lines": [55, 56, 57, 58, 59], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 47}, "_AsyncHttpClient._request": {"executed_lines": [82, 83, 87, 88, 89, 91, 92, 93, 97, 98, 100, 103, 104, 112, 113, 114, 115, 118, 119, 121, 124, 125, 133, 134, 135, 136, 143, 144, 145, 146, 147], "summary": {"covered_lines": 31, "num_statements": 33, "percent_covered": 93.93939393939394, "percent_covered_display": "94", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 93.93939393939394, "percent_statements_covered_display": "94"}, "missing_lines": [99, 101], "excluded_lines": [], "start_line": 61}, "_AsyncHttpClient.close": {"executed_lines": [154, 155, 156], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 149}, "": {"executed_lines": [12, 14, 15, 16, 18, 24, 47, 61, 149], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [20, 21], "start_line": 1}}, "classes": {"_AsyncHttpClient": {"executed_lines": [55, 56, 57, 58, 59, 82, 83, 87, 88, 89, 91, 92, 93, 97, 98, 100, 103, 104, 112, 113, 114, 115, 118, 119, 121, 124, 125, 133, 134, 135, 136, 143, 144, 145, 146, 147, 154, 155, 156], "summary": {"covered_lines": 39, "num_statements": 41, "percent_covered": 95.1219512195122, "percent_covered_display": "95", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 95.1219512195122, "percent_statements_covered_display": "95"}, "missing_lines": [99, 101], "excluded_lines": [], "start_line": 24}, "": {"executed_lines": [12, 14, 15, 16, 18, 24, 47, 61, 149], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [20, 21], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\data\\__init__.py": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\data\\_async_batch.py": {"executed_lines": [6, 8, 9, 11, 12, 13, 14, 15, 42, 45, 61, 62, 63, 64, 65, 67, 76, 90, 101, 102, 104, 106, 107, 115, 116, 118, 121, 122, 124, 125, 140, 141, 142, 146, 152, 158, 159, 160, 161, 162, 164, 165, 166, 167, 168, 170, 171, 173, 175, 176, 177, 179, 180, 181, 182, 183, 185, 187, 189, 191, 193, 195, 197, 199, 201, 203, 205, 207, 209, 214, 216, 217, 218, 222, 228, 229, 230, 231, 232, 234, 235, 236, 237, 238, 239, 240, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 255, 256, 258, 259, 260, 261, 262, 263, 264, 265, 273, 275, 276, 277, 281, 283, 284, 285, 287, 288, 289, 291, 292, 293, 294, 295, 296, 299, 300, 304, 305, 311, 312], "summary": {"covered_lines": 131, "num_statements": 149, "percent_covered": 87.91946308724832, "percent_covered_display": "88", "missing_lines": 18, "excluded_lines": 2, "percent_statements_covered": 87.91946308724832, "percent_statements_covered_display": "88"}, "missing_lines": [68, 108, 143, 144, 178, 184, 186, 188, 190, 192, 194, 196, 198, 200, 202, 204, 206, 208], "excluded_lines": [39, 40], "functions": {"_SyncResponseWrapper.__init__": {"executed_lines": [62, 63, 64, 65], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 61}, "_SyncResponseWrapper.json": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [68], "excluded_lines": [], "start_line": 67}, "_AsyncBatchClient.execute": {"executed_lines": [101, 102, 104, 106, 107, 115, 116, 118, 121, 122, 124, 125, 140, 141, 142, 146, 152], "summary": {"covered_lines": 17, "num_statements": 20, "percent_covered": 85.0, "percent_covered_display": "85", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 85.0, "percent_statements_covered_display": "85"}, "missing_lines": [108, 143, 144], "excluded_lines": [], "start_line": 90}, "_AsyncBatchClient._resolve_all": {"executed_lines": [159, 160, 161, 162, 164, 165, 166, 167, 168, 170, 171], "summary": {"covered_lines": 11, "num_statements": 11, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 158}, "_AsyncBatchClient._resolve_item": {"executed_lines": [175, 176, 177, 179, 180, 181, 182, 183, 185, 187, 189, 191, 193, 195, 197, 199, 201, 203, 205, 207, 209], "summary": {"covered_lines": 21, "num_statements": 35, "percent_covered": 60.0, "percent_covered_display": "60", "missing_lines": 14, "excluded_lines": 0, "percent_statements_covered": 60.0, "percent_statements_covered_display": "60"}, "missing_lines": [178, 184, 186, 188, 190, 192, 194, 196, 198, 200, 202, 204, 206, 208], "excluded_lines": [], "start_line": 173}, "_AsyncBatchClient._resolve_one": {"executed_lines": [216, 217, 218, 222], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 214}, "_AsyncBatchClient._resolve_record_create": {"executed_lines": [229, 230, 231, 232], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 228}, "_AsyncBatchClient._resolve_record_update": {"executed_lines": [235, 236, 237, 238, 239, 240], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 234}, "_AsyncBatchClient._resolve_record_delete": {"executed_lines": [243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253], "summary": {"covered_lines": 11, "num_statements": 11, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 242}, "_AsyncBatchClient._resolve_record_get": {"executed_lines": [256], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 255}, "_AsyncBatchClient._resolve_record_upsert": {"executed_lines": [259, 260, 261, 262, 263, 264, 265], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 258}, "_AsyncBatchClient._require_entity_metadata": {"executed_lines": [275, 276, 277, 281], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 273}, "_AsyncBatchClient._resolve_table_delete": {"executed_lines": [284, 285], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 283}, "_AsyncBatchClient._resolve_table_add_columns": {"executed_lines": [288, 289], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 287}, "_AsyncBatchClient._resolve_table_remove_columns": {"executed_lines": [292, 293, 294, 295, 296, 299, 300, 304, 305], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 291}, "_AsyncBatchClient._resolve_query_sql": {"executed_lines": [312], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 311}, "": {"executed_lines": [6, 8, 9, 11, 12, 13, 14, 15, 42, 45, 61, 67, 76, 90, 158, 173, 214, 228, 234, 242, 255, 258, 273, 283, 287, 291, 311], "summary": {"covered_lines": 27, "num_statements": 27, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [39, 40], "start_line": 1}}, "classes": {"_SyncResponseWrapper": {"executed_lines": [62, 63, 64, 65], "summary": {"covered_lines": 4, "num_statements": 5, "percent_covered": 80.0, "percent_covered_display": "80", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 80.0, "percent_statements_covered_display": "80"}, "missing_lines": [68], "excluded_lines": [], "start_line": 45}, "_AsyncBatchClient": {"executed_lines": [101, 102, 104, 106, 107, 115, 116, 118, 121, 122, 124, 125, 140, 141, 142, 146, 152, 159, 160, 161, 162, 164, 165, 166, 167, 168, 170, 171, 175, 176, 177, 179, 180, 181, 182, 183, 185, 187, 189, 191, 193, 195, 197, 199, 201, 203, 205, 207, 209, 216, 217, 218, 222, 229, 230, 231, 232, 235, 236, 237, 238, 239, 240, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 256, 259, 260, 261, 262, 263, 264, 265, 275, 276, 277, 281, 284, 285, 288, 289, 292, 293, 294, 295, 296, 299, 300, 304, 305, 312], "summary": {"covered_lines": 100, "num_statements": 117, "percent_covered": 85.47008547008546, "percent_covered_display": "85", "missing_lines": 17, "excluded_lines": 0, "percent_statements_covered": 85.47008547008546, "percent_statements_covered_display": "85"}, "missing_lines": [108, 143, 144, 178, 184, 186, 188, 190, 192, 194, 196, 198, 200, 202, 204, 206, 208], "excluded_lines": [], "start_line": 76}, "": {"executed_lines": [6, 8, 9, 11, 12, 13, 14, 15, 42, 45, 61, 67, 76, 90, 158, 173, 214, 228, 234, 242, 255, 258, 273, 283, 287, 291, 311], "summary": {"covered_lines": 27, "num_statements": 27, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [39, 40], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\data\\_async_odata.py": {"executed_lines": [6, 8, 10, 11, 12, 13, 14, 15, 17, 19, 21, 22, 23, 24, 25, 26, 39, 49, 52, 73, 74, 75, 83, 89, 90, 91, 93, 95, 96, 97, 106, 109, 120, 122, 123, 124, 129, 137, 138, 139, 141, 142, 143, 144, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 160, 161, 162, 163, 164, 165, 166, 167, 172, 173, 174, 175, 176, 177, 180, 181, 199, 210, 211, 212, 213, 214, 215, 218, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 250, 271, 272, 273, 274, 275, 278, 281, 282, 283, 285, 286, 288, 289, 290, 292, 293, 294, 295, 296, 297, 299, 325, 326, 327, 328, 329, 331, 362, 363, 367, 368, 369, 370, 371, 372, 373, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 388, 390, 391, 392, 393, 403, 421, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 439, 440, 441, 443, 458, 459, 460, 461, 465, 466, 467, 470, 471, 472, 474, 486, 488, 511, 512, 513, 514, 516, 527, 529, 547, 548, 550, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 597, 598, 599, 600, 601, 605, 606, 607, 608, 610, 611, 613, 614, 616, 617, 619, 620, 621, 622, 623, 625, 626, 627, 628, 630, 631, 632, 634, 635, 636, 637, 638, 639, 642, 661, 662, 663, 664, 665, 670, 672, 673, 674, 679, 680, 681, 682, 685, 686, 687, 690, 691, 692, 693, 694, 696, 697, 706, 707, 711, 712, 725, 726, 736, 737, 747, 748, 749, 750, 752, 753, 754, 756, 759, 764, 765, 768, 769, 770, 771, 772, 774, 775, 776, 780, 781, 782, 783, 786, 787, 792, 796, 797, 798, 799, 803, 804, 805, 806, 807, 810, 821, 823, 824, 825, 829, 830, 831, 833, 840, 841, 853, 854, 856, 857, 861, 865, 867, 869, 876, 877, 878, 879, 880, 881, 882, 883, 885, 886, 887, 888, 889, 893, 894, 895, 898, 899, 900, 901, 902, 903, 905, 933, 934, 935, 939, 940, 941, 942, 943, 944, 945, 946, 947, 949, 962, 963, 964, 965, 967, 968, 970, 971, 972, 973, 974, 975, 978, 983, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 998, 1006, 1007, 1008, 1009, 1010, 1012, 1013, 1018, 1019, 1020, 1022, 1023, 1024, 1026, 1027, 1029, 1030, 1031, 1032, 1033, 1034, 1036, 1037, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1050, 1052, 1061, 1064, 1068, 1069, 1072, 1075, 1076, 1077, 1079, 1081, 1082, 1083, 1084, 1086, 1087, 1088, 1089, 1090, 1091, 1092, 1093, 1094, 1096, 1105, 1106, 1107, 1108, 1118, 1145, 1146, 1148, 1160, 1161, 1162, 1166, 1170, 1197, 1198, 1199, 1204, 1205, 1206, 1210, 1212, 1213, 1215, 1221, 1235, 1236, 1237, 1242, 1243, 1244, 1245, 1247, 1263, 1264, 1265, 1270, 1271, 1272, 1274, 1304, 1305, 1306, 1311, 1315, 1316, 1318, 1322, 1323, 1324, 1325, 1326, 1327, 1328, 1329, 1331, 1332, 1333, 1334, 1335, 1337, 1338, 1339, 1341, 1348, 1358, 1378, 1379, 1381, 1382, 1383, 1388, 1389, 1390, 1392, 1393, 1394, 1395, 1399, 1400, 1401, 1406, 1407, 1409, 1410, 1412, 1414, 1435, 1436, 1437, 1438, 1440, 1442, 1443, 1444, 1446, 1447, 1448, 1454, 1455, 1456, 1457, 1459, 1460, 1461, 1462, 1467, 1468, 1469, 1471, 1473, 1474, 1475, 1476, 1477, 1479, 1481, 1482, 1484, 1488, 1497, 1498, 1499, 1506, 1513, 1515, 1516, 1517, 1518, 1519, 1520, 1521, 1522, 1523, 1529, 1542, 1543, 1544, 1545, 1547, 1548, 1549, 1557, 1569, 1570, 1571, 1572, 1573, 1574, 1575, 1576, 1577, 1583, 1591, 1592, 1593, 1594, 1595, 1596, 1600, 1602, 1603, 1605, 1617, 1618, 1619, 1620, 1621, 1627, 1635, 1636, 1641, 1642, 1643, 1644, 1645, 1646, 1647, 1650, 1651, 1655, 1656, 1657, 1658, 1659, 1660, 1666, 1677, 1678, 1680, 1681, 1682, 1689, 1691, 1692, 1693, 1694, 1725, 1731, 1739, 1740, 1741, 1742, 1743, 1745, 1760, 1761, 1762], "summary": {"covered_lines": 646, "num_statements": 699, "percent_covered": 92.41773962804005, "percent_covered_display": "92", "missing_lines": 53, "excluded_lines": 0, "percent_statements_covered": 92.41773962804005, "percent_statements_covered_display": "92"}, "missing_lines": [107, 125, 126, 127, 145, 146, 178, 179, 276, 277, 279, 395, 396, 397, 398, 399, 422, 438, 468, 469, 602, 603, 675, 676, 683, 713, 714, 723, 724, 727, 728, 735, 738, 739, 746, 751, 784, 785, 855, 862, 866, 884, 896, 897, 969, 1025, 1028, 1035, 1038, 1078, 1085, 1211, 1514], "excluded_lines": [], "functions": {"_AsyncODataClient.__init__": {"executed_lines": [73, 74, 75], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 52}, "_AsyncODataClient.close": {"executed_lines": [89, 90, 91], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 83}, "_AsyncODataClient._headers": {"executed_lines": [95, 96, 97], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 93}, "_AsyncODataClient._raw_request": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [107], "excluded_lines": [], "start_line": 106}, "_AsyncODataClient._request": {"executed_lines": [120, 122, 129, 137, 138, 139, 141, 142, 143, 144, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 160, 161, 162, 163, 164, 165, 166, 167, 172, 173, 174, 175, 176, 177, 180, 181], "summary": {"covered_lines": 38, "num_statements": 42, "percent_covered": 90.47619047619048, "percent_covered_display": "90", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 90.47619047619048, "percent_statements_covered_display": "90"}, "missing_lines": [145, 146, 178, 179], "excluded_lines": [], "start_line": 109}, "_AsyncODataClient._request._merge": {"executed_lines": [123, 124], "summary": {"covered_lines": 2, "num_statements": 5, "percent_covered": 40.0, "percent_covered_display": "40", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 40.0, "percent_statements_covered_display": "40"}, "missing_lines": [125, 126, 127], "excluded_lines": [], "start_line": 122}, "_AsyncODataClient._execute_raw": {"executed_lines": [210, 211, 212, 213, 214, 215], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 199}, "_AsyncODataClient._create": {"executed_lines": [234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 218}, "_AsyncODataClient._create_multiple": {"executed_lines": [271, 272, 273, 274, 275, 278, 281, 282, 283, 285, 286, 288, 289, 290, 292, 293, 294, 295, 296, 297], "summary": {"covered_lines": 20, "num_statements": 23, "percent_covered": 86.95652173913044, "percent_covered_display": "87", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 86.95652173913044, "percent_statements_covered_display": "87"}, "missing_lines": [276, 277, 279], "excluded_lines": [], "start_line": 250}, "_AsyncODataClient._upsert": {"executed_lines": [325, 326, 327, 328, 329], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 299}, "_AsyncODataClient._upsert_multiple": {"executed_lines": [362, 363, 367, 368, 369, 370, 371, 372, 373, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385], "summary": {"covered_lines": 19, "num_statements": 19, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 331}, "_AsyncODataClient._primary_id_attr": {"executed_lines": [390, 391, 392, 393], "summary": {"covered_lines": 4, "num_statements": 9, "percent_covered": 44.44444444444444, "percent_covered_display": "44", "missing_lines": 5, "excluded_lines": 0, "percent_statements_covered": 44.44444444444444, "percent_statements_covered_display": "44"}, "missing_lines": [395, 396, 397, 398, 399], "excluded_lines": [], "start_line": 388}, "_AsyncODataClient._update_by_ids": {"executed_lines": [421, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 439, 440, 441], "summary": {"covered_lines": 19, "num_statements": 21, "percent_covered": 90.47619047619048, "percent_covered_display": "90", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 90.47619047619048, "percent_statements_covered_display": "90"}, "missing_lines": [422, 438], "excluded_lines": [], "start_line": 403}, "_AsyncODataClient._delete_multiple": {"executed_lines": [458, 459, 460, 461, 465, 466, 467, 470, 471, 472], "summary": {"covered_lines": 10, "num_statements": 12, "percent_covered": 83.33333333333333, "percent_covered_display": "83", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 83.33333333333333, "percent_statements_covered_display": "83"}, "missing_lines": [468, 469], "excluded_lines": [], "start_line": 443}, "_AsyncODataClient._update": {"executed_lines": [486], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 474}, "_AsyncODataClient._update_multiple": {"executed_lines": [511, 512, 513, 514], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 488}, "_AsyncODataClient._delete": {"executed_lines": [527], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 516}, "_AsyncODataClient._get": {"executed_lines": [547, 548], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 529}, "_AsyncODataClient._get_multiple": {"executed_lines": [586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 597, 605, 606, 607, 608, 610, 611, 613, 614, 616, 617, 619, 620, 621, 622, 623, 625, 626, 627, 628, 630, 631, 632, 634, 635, 636, 637, 638, 639], "summary": {"covered_lines": 39, "num_statements": 39, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 550}, "_AsyncODataClient._get_multiple._do_request": {"executed_lines": [598, 599, 600, 601], "summary": {"covered_lines": 4, "num_statements": 6, "percent_covered": 66.66666666666667, "percent_covered_display": "67", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 66.66666666666667, "percent_statements_covered_display": "67"}, "missing_lines": [602, 603], "excluded_lines": [], "start_line": 597}, "_AsyncODataClient._query_sql": {"executed_lines": [661, 662, 663, 664, 665, 670, 672, 673, 674, 679, 680, 681, 682, 685, 686, 687, 690, 691, 692, 693, 694, 696, 697, 706, 707, 711, 712, 725, 726, 736, 737, 747, 748, 749, 750, 752, 753, 754, 756], "summary": {"covered_lines": 39, "num_statements": 53, "percent_covered": 73.58490566037736, "percent_covered_display": "74", "missing_lines": 14, "excluded_lines": 0, "percent_statements_covered": 73.58490566037736, "percent_statements_covered_display": "74"}, "missing_lines": [675, 676, 683, 713, 714, 723, 724, 727, 728, 735, 738, 739, 746, 751], "excluded_lines": [], "start_line": 642}, "_AsyncODataClient._entity_set_from_schema_name": {"executed_lines": [764, 765, 768, 769, 770, 771, 772, 774, 775, 776, 780, 781, 782, 783, 786, 787, 792, 796, 797, 798, 799, 803, 804, 805, 806, 807], "summary": {"covered_lines": 26, "num_statements": 28, "percent_covered": 92.85714285714286, "percent_covered_display": "93", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 92.85714285714286, "percent_statements_covered_display": "93"}, "missing_lines": [784, 785], "excluded_lines": [], "start_line": 759}, "_AsyncODataClient._get_entity_by_table_schema_name": {"executed_lines": [821, 823, 824, 825, 829, 830, 831], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 810}, "_AsyncODataClient._create_entity": {"executed_lines": [840, 841, 853, 854, 856, 857, 861, 865, 867], "summary": {"covered_lines": 9, "num_statements": 12, "percent_covered": 75.0, "percent_covered_display": "75", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 75.0, "percent_statements_covered_display": "75"}, "missing_lines": [855, 862, 866], "excluded_lines": [], "start_line": 833}, "_AsyncODataClient._get_attribute_metadata": {"executed_lines": [876, 877, 878, 879, 880, 881, 882, 883, 885, 886, 887, 888, 889, 893, 894, 895, 898, 899, 900, 901, 902, 903], "summary": {"covered_lines": 22, "num_statements": 25, "percent_covered": 88.0, "percent_covered_display": "88", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 88.0, "percent_statements_covered_display": "88"}, "missing_lines": [884, 896, 897], "excluded_lines": [], "start_line": 869}, "_AsyncODataClient._list_columns": {"executed_lines": [933, 934, 935, 939, 940, 941, 942, 943, 944, 945, 946, 947], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 905}, "_AsyncODataClient._wait_for_attribute_visibility": {"executed_lines": [962, 963, 964, 965, 967, 968, 970, 971, 972, 973, 974, 975, 978], "summary": {"covered_lines": 13, "num_statements": 14, "percent_covered": 92.85714285714286, "percent_covered_display": "93", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 92.85714285714286, "percent_statements_covered_display": "93"}, "missing_lines": [969], "excluded_lines": [], "start_line": 949}, "_AsyncODataClient._request_metadata_with_retry": {"executed_lines": [985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 983}, "_AsyncODataClient._bulk_fetch_picklists": {"executed_lines": [1006, 1007, 1008, 1009, 1010, 1012, 1013, 1018, 1019, 1020, 1022, 1023, 1024, 1026, 1027, 1029, 1030, 1031, 1032, 1033, 1034, 1036, 1037, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1050], "summary": {"covered_lines": 34, "num_statements": 38, "percent_covered": 89.47368421052632, "percent_covered_display": "89", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 89.47368421052632, "percent_statements_covered_display": "89"}, "missing_lines": [1025, 1028, 1035, 1038], "excluded_lines": [], "start_line": 998}, "_AsyncODataClient._convert_labels_to_ints": {"executed_lines": [1061, 1064, 1068, 1069, 1072, 1075, 1076, 1077, 1079, 1081, 1082, 1083, 1084, 1086, 1087, 1088, 1089, 1090, 1091, 1092, 1093, 1094], "summary": {"covered_lines": 22, "num_statements": 24, "percent_covered": 91.66666666666667, "percent_covered_display": "92", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 91.66666666666667, "percent_statements_covered_display": "92"}, "missing_lines": [1078, 1085], "excluded_lines": [], "start_line": 1052}, "_AsyncODataClient._get_table_info": {"executed_lines": [1105, 1106, 1107, 1108], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1096}, "_AsyncODataClient._list_tables": {"executed_lines": [1145, 1146], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1118}, "_AsyncODataClient._delete_table": {"executed_lines": [1160, 1161, 1162, 1166], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1148}, "_AsyncODataClient._create_alternate_key": {"executed_lines": [1197, 1198, 1199, 1204, 1205, 1206, 1210, 1212, 1213, 1215], "summary": {"covered_lines": 10, "num_statements": 11, "percent_covered": 90.9090909090909, "percent_covered_display": "91", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 90.9090909090909, "percent_statements_covered_display": "91"}, "missing_lines": [1211], "excluded_lines": [], "start_line": 1170}, "_AsyncODataClient._get_alternate_keys": {"executed_lines": [1235, 1236, 1237, 1242, 1243, 1244, 1245], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1221}, "_AsyncODataClient._delete_alternate_key": {"executed_lines": [1263, 1264, 1265, 1270, 1271, 1272], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1247}, "_AsyncODataClient._create_table": {"executed_lines": [1304, 1305, 1306, 1311, 1315, 1316, 1318, 1322, 1323, 1324, 1325, 1326, 1327, 1328, 1329, 1331, 1332, 1333, 1334, 1335, 1337, 1338, 1339, 1341, 1348], "summary": {"covered_lines": 25, "num_statements": 25, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1274}, "_AsyncODataClient._create_columns": {"executed_lines": [1378, 1379, 1381, 1382, 1383, 1388, 1389, 1390, 1392, 1393, 1394, 1395, 1399, 1400, 1401, 1406, 1407, 1409, 1410, 1412], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1358}, "_AsyncODataClient._delete_columns": {"executed_lines": [1435, 1436, 1437, 1438, 1440, 1442, 1443, 1444, 1446, 1447, 1448, 1454, 1455, 1456, 1457, 1459, 1460, 1461, 1462, 1467, 1468, 1469, 1471, 1473, 1474, 1475, 1476, 1477, 1479, 1481, 1482, 1484], "summary": {"covered_lines": 32, "num_statements": 32, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1414}, "_AsyncODataClient._build_create": {"executed_lines": [1497, 1498, 1499], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1488}, "_AsyncODataClient._build_create_multiple": {"executed_lines": [1513, 1515, 1516, 1517, 1518, 1519, 1520, 1521, 1522, 1523], "summary": {"covered_lines": 10, "num_statements": 11, "percent_covered": 90.9090909090909, "percent_covered_display": "91", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 90.9090909090909, "percent_statements_covered_display": "91"}, "missing_lines": [1514], "excluded_lines": [], "start_line": 1506}, "_AsyncODataClient._build_update": {"executed_lines": [1542, 1543, 1544, 1545, 1547, 1548, 1549], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1529}, "_AsyncODataClient._build_update_multiple_from_records": {"executed_lines": [1569, 1570, 1571, 1572, 1573, 1574, 1575, 1576, 1577], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1557}, "_AsyncODataClient._build_update_multiple": {"executed_lines": [1591, 1592, 1593, 1594, 1595, 1596, 1600, 1602, 1603], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1583}, "_AsyncODataClient._build_upsert": {"executed_lines": [1617, 1618, 1619, 1620, 1621], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1605}, "_AsyncODataClient._build_upsert_multiple": {"executed_lines": [1635, 1636, 1641, 1642, 1643, 1644, 1645, 1646, 1647, 1650, 1651, 1655, 1656, 1657, 1658, 1659, 1660], "summary": {"covered_lines": 17, "num_statements": 17, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1627}, "_AsyncODataClient._build_delete": {"executed_lines": [1677, 1678, 1680, 1681, 1682], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1666}, "_AsyncODataClient._build_delete_multiple": {"executed_lines": [1691, 1692, 1693, 1694, 1725], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1689}, "_AsyncODataClient._build_get": {"executed_lines": [1739, 1740, 1741, 1742, 1743], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1731}, "_AsyncODataClient._build_sql": {"executed_lines": [1760, 1761, 1762], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1745}, "": {"executed_lines": [6, 8, 10, 11, 12, 13, 14, 15, 17, 19, 21, 22, 23, 24, 25, 26, 39, 49, 52, 83, 93, 106, 109, 199, 218, 250, 299, 331, 388, 403, 443, 474, 488, 516, 529, 550, 642, 759, 810, 833, 869, 905, 949, 983, 998, 1052, 1096, 1118, 1148, 1170, 1221, 1247, 1274, 1358, 1414, 1488, 1506, 1529, 1557, 1583, 1605, 1627, 1666, 1689, 1731, 1745], "summary": {"covered_lines": 66, "num_statements": 66, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_AsyncODataClient": {"executed_lines": [73, 74, 75, 89, 90, 91, 95, 96, 97, 120, 122, 123, 124, 129, 137, 138, 139, 141, 142, 143, 144, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 160, 161, 162, 163, 164, 165, 166, 167, 172, 173, 174, 175, 176, 177, 180, 181, 210, 211, 212, 213, 214, 215, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 271, 272, 273, 274, 275, 278, 281, 282, 283, 285, 286, 288, 289, 290, 292, 293, 294, 295, 296, 297, 325, 326, 327, 328, 329, 362, 363, 367, 368, 369, 370, 371, 372, 373, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 390, 391, 392, 393, 421, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 439, 440, 441, 458, 459, 460, 461, 465, 466, 467, 470, 471, 472, 486, 511, 512, 513, 514, 527, 547, 548, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 597, 598, 599, 600, 601, 605, 606, 607, 608, 610, 611, 613, 614, 616, 617, 619, 620, 621, 622, 623, 625, 626, 627, 628, 630, 631, 632, 634, 635, 636, 637, 638, 639, 661, 662, 663, 664, 665, 670, 672, 673, 674, 679, 680, 681, 682, 685, 686, 687, 690, 691, 692, 693, 694, 696, 697, 706, 707, 711, 712, 725, 726, 736, 737, 747, 748, 749, 750, 752, 753, 754, 756, 764, 765, 768, 769, 770, 771, 772, 774, 775, 776, 780, 781, 782, 783, 786, 787, 792, 796, 797, 798, 799, 803, 804, 805, 806, 807, 821, 823, 824, 825, 829, 830, 831, 840, 841, 853, 854, 856, 857, 861, 865, 867, 876, 877, 878, 879, 880, 881, 882, 883, 885, 886, 887, 888, 889, 893, 894, 895, 898, 899, 900, 901, 902, 903, 933, 934, 935, 939, 940, 941, 942, 943, 944, 945, 946, 947, 962, 963, 964, 965, 967, 968, 970, 971, 972, 973, 974, 975, 978, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 1006, 1007, 1008, 1009, 1010, 1012, 1013, 1018, 1019, 1020, 1022, 1023, 1024, 1026, 1027, 1029, 1030, 1031, 1032, 1033, 1034, 1036, 1037, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1050, 1061, 1064, 1068, 1069, 1072, 1075, 1076, 1077, 1079, 1081, 1082, 1083, 1084, 1086, 1087, 1088, 1089, 1090, 1091, 1092, 1093, 1094, 1105, 1106, 1107, 1108, 1145, 1146, 1160, 1161, 1162, 1166, 1197, 1198, 1199, 1204, 1205, 1206, 1210, 1212, 1213, 1215, 1235, 1236, 1237, 1242, 1243, 1244, 1245, 1263, 1264, 1265, 1270, 1271, 1272, 1304, 1305, 1306, 1311, 1315, 1316, 1318, 1322, 1323, 1324, 1325, 1326, 1327, 1328, 1329, 1331, 1332, 1333, 1334, 1335, 1337, 1338, 1339, 1341, 1348, 1378, 1379, 1381, 1382, 1383, 1388, 1389, 1390, 1392, 1393, 1394, 1395, 1399, 1400, 1401, 1406, 1407, 1409, 1410, 1412, 1435, 1436, 1437, 1438, 1440, 1442, 1443, 1444, 1446, 1447, 1448, 1454, 1455, 1456, 1457, 1459, 1460, 1461, 1462, 1467, 1468, 1469, 1471, 1473, 1474, 1475, 1476, 1477, 1479, 1481, 1482, 1484, 1497, 1498, 1499, 1513, 1515, 1516, 1517, 1518, 1519, 1520, 1521, 1522, 1523, 1542, 1543, 1544, 1545, 1547, 1548, 1549, 1569, 1570, 1571, 1572, 1573, 1574, 1575, 1576, 1577, 1591, 1592, 1593, 1594, 1595, 1596, 1600, 1602, 1603, 1617, 1618, 1619, 1620, 1621, 1635, 1636, 1641, 1642, 1643, 1644, 1645, 1646, 1647, 1650, 1651, 1655, 1656, 1657, 1658, 1659, 1660, 1677, 1678, 1680, 1681, 1682, 1691, 1692, 1693, 1694, 1725, 1739, 1740, 1741, 1742, 1743, 1760, 1761, 1762], "summary": {"covered_lines": 580, "num_statements": 633, "percent_covered": 91.62717219589257, "percent_covered_display": "92", "missing_lines": 53, "excluded_lines": 0, "percent_statements_covered": 91.62717219589257, "percent_statements_covered_display": "92"}, "missing_lines": [107, 125, 126, 127, 145, 146, 178, 179, 276, 277, 279, 395, 396, 397, 398, 399, 422, 438, 468, 469, 602, 603, 675, 676, 683, 713, 714, 723, 724, 727, 728, 735, 738, 739, 746, 751, 784, 785, 855, 862, 866, 884, 896, 897, 969, 1025, 1028, 1035, 1038, 1078, 1085, 1211, 1514], "excluded_lines": [], "start_line": 49}, "": {"executed_lines": [6, 8, 10, 11, 12, 13, 14, 15, 17, 19, 21, 22, 23, 24, 25, 26, 39, 49, 52, 83, 93, 106, 109, 199, 218, 250, 299, 331, 388, 403, 443, 474, 488, 516, 529, 550, 642, 759, 810, 833, 869, 905, 949, 983, 998, 1052, 1096, 1118, 1148, 1170, 1221, 1247, 1274, 1358, 1414, 1488, 1506, 1529, 1557, 1583, 1605, 1627, 1666, 1689, 1731, 1745], "summary": {"covered_lines": 66, "num_statements": 66, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\data\\_async_relationships.py": {"executed_lines": [10, 12, 14, 15, 18, 28, 51, 54, 55, 57, 58, 59, 61, 64, 66, 74, 94, 96, 98, 99, 100, 102, 105, 107, 114, 123, 124, 125, 126, 128, 140, 141, 142, 143, 144, 145, 147, 171, 172, 173, 174, 175, 176, 177, 178, 180, 210, 211, 213, 214, 215, 220, 228, 229, 230, 231, 232, 233, 234, 236, 237, 238, 240, 241, 242, 244, 250, 260, 261, 262, 263], "summary": {"covered_lines": 71, "num_statements": 71, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"_AsyncRelationshipOperationsMixin._create_one_to_many_relationship": {"executed_lines": [51, 54, 55, 57, 58, 59, 61, 64, 66], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 28}, "_AsyncRelationshipOperationsMixin._create_many_to_many_relationship": {"executed_lines": [94, 96, 98, 99, 100, 102, 105, 107], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 74}, "_AsyncRelationshipOperationsMixin._delete_relationship": {"executed_lines": [123, 124, 125, 126], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 114}, "_AsyncRelationshipOperationsMixin._get_relationship": {"executed_lines": [140, 141, 142, 143, 144, 145], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 128}, "_AsyncRelationshipOperationsMixin._list_relationships": {"executed_lines": [171, 172, 173, 174, 175, 176, 177, 178], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 147}, "_AsyncRelationshipOperationsMixin._list_table_relationships": {"executed_lines": [210, 211, 213, 214, 215, 220, 228, 229, 230, 231, 232, 233, 234, 236, 237, 238, 240, 241, 242, 244], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 180}, "_AsyncRelationshipOperationsMixin._extract_id_from_header": {"executed_lines": [260, 261, 262, 263], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 250}, "": {"executed_lines": [10, 12, 14, 15, 18, 28, 74, 114, 128, 147, 180, 250], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_AsyncRelationshipOperationsMixin": {"executed_lines": [51, 54, 55, 57, 58, 59, 61, 64, 66, 94, 96, 98, 99, 100, 102, 105, 107, 123, 124, 125, 126, 140, 141, 142, 143, 144, 145, 171, 172, 173, 174, 175, 176, 177, 178, 210, 211, 213, 214, 215, 220, 228, 229, 230, 231, 232, 233, 234, 236, 237, 238, 240, 241, 242, 244, 260, 261, 262, 263], "summary": {"covered_lines": 59, "num_statements": 59, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 18}, "": {"executed_lines": [10, 12, 14, 15, 18, 28, 74, 114, 128, 147, 180, 250], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\data\\_async_upload.py": {"executed_lines": [6, 8, 11, 14, 43, 46, 49, 50, 51, 52, 53, 54, 56, 59, 61, 63, 64, 65, 70, 72, 73, 76, 77, 78, 80, 90, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 109, 110, 112, 114, 115, 117, 147, 148, 150, 151, 152, 153, 154, 155, 156, 157, 158, 161, 162, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 177, 178, 179, 180, 181, 182, 184, 185, 186, 193, 194, 195], "summary": {"covered_lines": 83, "num_statements": 87, "percent_covered": 95.40229885057471, "percent_covered_display": "95", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 95.40229885057471, "percent_statements_covered_display": "95"}, "missing_lines": [66, 67, 176, 183], "excluded_lines": [], "functions": {"_AsyncFileUploadMixin._upload_file": {"executed_lines": [43, 46, 49, 50, 51, 52, 53, 54, 56, 59, 61, 63, 64, 65, 70, 72, 73, 76, 77, 78], "summary": {"covered_lines": 20, "num_statements": 22, "percent_covered": 90.9090909090909, "percent_covered_display": "91", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 90.9090909090909, "percent_statements_covered_display": "91"}, "missing_lines": [66, 67], "excluded_lines": [], "start_line": 14}, "_AsyncFileUploadMixin._upload_file_small": {"executed_lines": [90, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 109, 110, 112, 114, 115], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 80}, "_AsyncFileUploadMixin._upload_file_chunk": {"executed_lines": [147, 148, 150, 151, 152, 153, 154, 155, 156, 157, 158, 161, 162, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 177, 178, 179, 180, 181, 182, 184, 185, 186, 193, 194, 195], "summary": {"covered_lines": 37, "num_statements": 39, "percent_covered": 94.87179487179488, "percent_covered_display": "95", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 94.87179487179488, "percent_statements_covered_display": "95"}, "missing_lines": [176, 183], "excluded_lines": [], "start_line": 117}, "": {"executed_lines": [6, 8, 11, 14, 80, 117], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_AsyncFileUploadMixin": {"executed_lines": [43, 46, 49, 50, 51, 52, 53, 54, 56, 59, 61, 63, 64, 65, 70, 72, 73, 76, 77, 78, 90, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 109, 110, 112, 114, 115, 147, 148, 150, 151, 152, 153, 154, 155, 156, 157, 158, 161, 162, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 177, 178, 179, 180, 181, 182, 184, 185, 186, 193, 194, 195], "summary": {"covered_lines": 77, "num_statements": 81, "percent_covered": 95.06172839506173, "percent_covered_display": "95", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 95.06172839506173, "percent_statements_covered_display": "95"}, "missing_lines": [66, 67, 176, 183], "excluded_lines": [], "start_line": 11}, "": {"executed_lines": [6, 8, 11, 14, 80, 117], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\operations\\__init__.py": {"executed_lines": [11, 13], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [11, 13], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [11, 13], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\operations\\async_batch.py": {"executed_lines": [6, 8, 10, 12, 13, 14, 34, 35, 36, 37, 42, 47, 64, 75, 76, 78, 101, 103, 116, 118, 128, 131, 149, 150, 151, 153, 154, 156, 157, 165, 177, 178, 180, 197, 199, 219, 221, 243, 245, 265, 267, 286, 287, 288, 289, 290, 291, 292, 293, 295, 296, 299, 315, 316, 318, 341, 351, 360, 362, 369, 371, 385, 387, 398, 400, 412, 414, 431, 433, 447, 449, 456, 458, 465, 467, 503, 523, 534, 535, 537, 554, 555, 556, 564, 574, 575, 577, 591, 592, 593, 594, 596, 598, 599, 600, 601, 605, 607, 629, 630, 631, 632, 633, 634, 636, 637, 638, 639, 643, 645, 646, 647, 651, 653, 654, 655, 656, 657, 658, 660, 662, 681, 682, 683, 684, 685, 686, 687, 688, 689, 690, 698, 727, 728, 729, 730, 731, 732, 733, 734, 736, 751, 752, 753, 755, 773, 774, 777, 793, 794, 796, 802], "summary": {"covered_lines": 152, "num_statements": 152, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [44, 45], "functions": {"AsyncChangeSetRecordOperations.__init__": {"executed_lines": [76], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 75}, "AsyncChangeSetRecordOperations.create": {"executed_lines": [101], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 78}, "AsyncChangeSetRecordOperations.update": {"executed_lines": [116], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 103}, "AsyncChangeSetRecordOperations.delete": {"executed_lines": [128], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 118}, "AsyncChangeSet.__init__": {"executed_lines": [150, 151], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 149}, "AsyncChangeSet.__aenter__": {"executed_lines": [154], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 153}, "AsyncChangeSet.__aexit__": {"executed_lines": [157], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 156}, "AsyncBatchRecordOperations.__init__": {"executed_lines": [178], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 177}, "AsyncBatchRecordOperations.create": {"executed_lines": [197], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 180}, "AsyncBatchRecordOperations.update": {"executed_lines": [219], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 199}, "AsyncBatchRecordOperations.delete": {"executed_lines": [243], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 221}, "AsyncBatchRecordOperations.get": {"executed_lines": [265], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 245}, "AsyncBatchRecordOperations.upsert": {"executed_lines": [286, 287, 288, 289, 290, 291, 292, 293, 295, 296], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 267}, "AsyncBatchTableOperations.__init__": {"executed_lines": [316], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 315}, "AsyncBatchTableOperations.create": {"executed_lines": [341], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 318}, "AsyncBatchTableOperations.delete": {"executed_lines": [360], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 351}, "AsyncBatchTableOperations.get": {"executed_lines": [369], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 362}, "AsyncBatchTableOperations.list": {"executed_lines": [385], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 371}, "AsyncBatchTableOperations.add_columns": {"executed_lines": [398], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 387}, "AsyncBatchTableOperations.remove_columns": {"executed_lines": [412], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 400}, "AsyncBatchTableOperations.create_one_to_many_relationship": {"executed_lines": [431], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 414}, "AsyncBatchTableOperations.create_many_to_many_relationship": {"executed_lines": [447], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 433}, "AsyncBatchTableOperations.delete_relationship": {"executed_lines": [456], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 449}, "AsyncBatchTableOperations.get_relationship": {"executed_lines": [465], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 458}, "AsyncBatchTableOperations.create_lookup_field": {"executed_lines": [503], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 467}, "AsyncBatchQueryOperations.__init__": {"executed_lines": [535], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 534}, "AsyncBatchQueryOperations.sql": {"executed_lines": [554, 555, 556], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 537}, "AsyncBatchDataFrameOperations.__init__": {"executed_lines": [575], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 574}, "AsyncBatchDataFrameOperations.create": {"executed_lines": [591, 592, 593, 594, 596, 598, 599, 600, 601, 605], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 577}, "AsyncBatchDataFrameOperations.update": {"executed_lines": [629, 630, 631, 632, 633, 634, 636, 637, 638, 639, 643, 645, 646, 647, 651, 653, 654, 655, 656, 657, 658, 660], "summary": {"covered_lines": 22, "num_statements": 22, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 607}, "AsyncBatchDataFrameOperations.delete": {"executed_lines": [681, 682, 683, 684, 685, 686, 687, 688, 689, 690], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 662}, "AsyncBatchRequest.__init__": {"executed_lines": [728, 729, 730, 731, 732, 733, 734], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 727}, "AsyncBatchRequest.changeset": {"executed_lines": [751, 752, 753], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 736}, "AsyncBatchRequest.execute": {"executed_lines": [773, 774], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 755}, "AsyncBatchOperations.__init__": {"executed_lines": [794], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 793}, "AsyncBatchOperations.new": {"executed_lines": [802], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 796}, "": {"executed_lines": [6, 8, 10, 12, 13, 14, 34, 35, 36, 37, 42, 47, 64, 75, 78, 103, 118, 131, 149, 153, 156, 165, 177, 180, 199, 221, 245, 267, 299, 315, 318, 351, 362, 371, 387, 400, 414, 433, 449, 458, 467, 523, 534, 537, 564, 574, 577, 607, 662, 698, 727, 736, 755, 777, 793, 796], "summary": {"covered_lines": 56, "num_statements": 56, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [44, 45], "start_line": 1}}, "classes": {"AsyncChangeSetRecordOperations": {"executed_lines": [76, 101, 116, 128], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 64}, "AsyncChangeSet": {"executed_lines": [150, 151, 154, 157], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 131}, "AsyncBatchRecordOperations": {"executed_lines": [178, 197, 219, 243, 265, 286, 287, 288, 289, 290, 291, 292, 293, 295, 296], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 165}, "AsyncBatchTableOperations": {"executed_lines": [316, 341, 360, 369, 385, 398, 412, 431, 447, 456, 465, 503], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 299}, "AsyncBatchQueryOperations": {"executed_lines": [535, 554, 555, 556], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 523}, "AsyncBatchDataFrameOperations": {"executed_lines": [575, 591, 592, 593, 594, 596, 598, 599, 600, 601, 605, 629, 630, 631, 632, 633, 634, 636, 637, 638, 639, 643, 645, 646, 647, 651, 653, 654, 655, 656, 657, 658, 660, 681, 682, 683, 684, 685, 686, 687, 688, 689, 690], "summary": {"covered_lines": 43, "num_statements": 43, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 564}, "AsyncBatchRequest": {"executed_lines": [728, 729, 730, 731, 732, 733, 734, 751, 752, 753, 773, 774], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 698}, "AsyncBatchOperations": {"executed_lines": [794, 802], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 777}, "": {"executed_lines": [6, 8, 10, 12, 13, 14, 34, 35, 36, 37, 42, 47, 64, 75, 78, 103, 118, 131, 149, 153, 156, 165, 177, 180, 199, 221, 245, 267, 299, 315, 318, 351, 362, 371, 387, 400, 414, 433, 449, 458, 467, 523, 534, 537, 564, 574, 577, 607, 662, 698, 727, 736, 755, 777, 793, 796], "summary": {"covered_lines": 56, "num_statements": 56, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [44, 45], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\operations\\async_dataframe.py": {"executed_lines": [6, 8, 10, 12, 18, 21, 51, 52, 56, 82, 83, 84, 85, 89, 151, 152, 153, 154, 155, 156, 160, 165, 167, 168, 179, 181, 182, 183, 187, 217, 218, 220, 221, 223, 226, 227, 228, 233, 235, 236, 238, 242, 280, 281, 282, 283, 284, 285, 287, 288, 289, 290, 294, 296, 297, 298, 301, 304, 305, 306, 307, 308, 310, 311, 313, 317, 350, 351, 353, 354, 355, 357, 358, 359, 363, 365, 366, 367, 368], "summary": {"covered_lines": 79, "num_statements": 79, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [14, 15], "functions": {"AsyncDataFrameOperations.__init__": {"executed_lines": [52], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 51}, "AsyncDataFrameOperations.sql": {"executed_lines": [82, 83, 84, 85], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 56}, "AsyncDataFrameOperations.get": {"executed_lines": [151, 152, 153, 154, 155, 156, 160, 165, 167, 168, 179, 181, 182, 183], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 89}, "AsyncDataFrameOperations.create": {"executed_lines": [217, 218, 220, 221, 223, 226, 227, 228, 233, 235, 236, 238], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 187}, "AsyncDataFrameOperations.update": {"executed_lines": [280, 281, 282, 283, 284, 285, 287, 288, 289, 290, 294, 296, 297, 298, 301, 304, 305, 306, 307, 308, 310, 311, 313], "summary": {"covered_lines": 23, "num_statements": 23, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 242}, "AsyncDataFrameOperations.delete": {"executed_lines": [350, 351, 353, 354, 355, 357, 358, 359, 363, 365, 366, 367, 368], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 317}, "": {"executed_lines": [6, 8, 10, 12, 18, 21, 51, 56, 89, 187, 242, 317], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [14, 15], "start_line": 1}}, "classes": {"AsyncDataFrameOperations": {"executed_lines": [52, 82, 83, 84, 85, 151, 152, 153, 154, 155, 156, 160, 165, 167, 168, 179, 181, 182, 183, 217, 218, 220, 221, 223, 226, 227, 228, 233, 235, 236, 238, 280, 281, 282, 283, 284, 285, 287, 288, 289, 290, 294, 296, 297, 298, 301, 304, 305, 306, 307, 308, 310, 311, 313, 350, 351, 353, 354, 355, 357, 358, 359, 363, 365, 366, 367, 368], "summary": {"covered_lines": 67, "num_statements": 67, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 21}, "": {"executed_lines": [6, 8, 10, 12, 18, 21, 51, 56, 89, 187, 242, 317], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [14, 15], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\operations\\async_files.py": {"executed_lines": [6, 8, 14, 17, 35, 36, 40, 104, 105], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [10, 11], "functions": {"AsyncFileOperations.__init__": {"executed_lines": [36], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 35}, "AsyncFileOperations.upload": {"executed_lines": [104, 105], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 40}, "": {"executed_lines": [6, 8, 14, 17, 35, 40], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [10, 11], "start_line": 1}}, "classes": {"AsyncFileOperations": {"executed_lines": [36, 104, 105], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 17}, "": {"executed_lines": [6, 8, 14, 17, 35, 40], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [10, 11], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\operations\\async_query.py": {"executed_lines": [6, 8, 10, 11, 17, 20, 39, 40, 44, 93, 94, 95, 99, 129, 138, 151, 152, 153, 154, 155, 156, 157, 160, 161, 163, 164, 165, 166, 167, 168, 169, 178, 179, 183, 208, 209, 213, 246, 247, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 267, 268, 269, 270, 271, 272, 273, 274, 276, 286, 287, 291, 327, 328, 329, 330, 331, 336, 337, 338, 339, 347, 372, 373, 377, 407, 408, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 423, 424, 425, 426, 427, 428, 430, 440, 441, 445, 477, 478, 479, 480, 481, 486, 490, 526, 527, 528, 529, 530, 535, 536, 537, 538], "summary": {"covered_lines": 115, "num_statements": 115, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [13, 14], "functions": {"AsyncQueryOperations.__init__": {"executed_lines": [40], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 39}, "AsyncQueryOperations.sql": {"executed_lines": [93, 94, 95], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 44}, "AsyncQueryOperations.sql_columns": {"executed_lines": [129, 138, 151, 152, 153, 154, 155, 156, 157, 160, 161, 163, 164, 165, 166, 167, 168, 169, 178, 179], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 99}, "AsyncQueryOperations.sql_select": {"executed_lines": [208, 209], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 183}, "AsyncQueryOperations.sql_joins": {"executed_lines": [246, 247, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 267, 268, 269, 270, 271, 272, 273, 274, 276, 286, 287], "summary": {"covered_lines": 25, "num_statements": 25, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 213}, "AsyncQueryOperations.sql_join": {"executed_lines": [327, 328, 329, 330, 331, 336, 337, 338, 339], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 291}, "AsyncQueryOperations.odata_select": {"executed_lines": [372, 373], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 347}, "AsyncQueryOperations.odata_expands": {"executed_lines": [407, 408, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 423, 424, 425, 426, 427, 428, 430, 440, 441], "summary": {"covered_lines": 22, "num_statements": 22, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 377}, "AsyncQueryOperations.odata_expand": {"executed_lines": [477, 478, 479, 480, 481, 486], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 445}, "AsyncQueryOperations.odata_bind": {"executed_lines": [526, 527, 528, 529, 530, 535, 536, 537, 538], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 490}, "": {"executed_lines": [6, 8, 10, 11, 17, 20, 39, 44, 99, 183, 213, 291, 347, 377, 445, 490], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [13, 14], "start_line": 1}}, "classes": {"AsyncQueryOperations": {"executed_lines": [40, 93, 94, 95, 129, 138, 151, 152, 153, 154, 155, 156, 157, 160, 161, 163, 164, 165, 166, 167, 168, 169, 178, 179, 208, 209, 246, 247, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 267, 268, 269, 270, 271, 272, 273, 274, 276, 286, 287, 327, 328, 329, 330, 331, 336, 337, 338, 339, 372, 373, 407, 408, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 423, 424, 425, 426, 427, 428, 430, 440, 441, 477, 478, 479, 480, 481, 486, 526, 527, 528, 529, 530, 535, 536, 537, 538], "summary": {"covered_lines": 99, "num_statements": 99, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 20}, "": {"executed_lines": [6, 8, 10, 11, 17, 20, 39, 44, 99, 183, 213, 291, 347, 377, 445, 490], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [13, 14], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\operations\\async_records.py": {"executed_lines": [6, 8, 10, 11, 17, 20, 46, 47, 57, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 110, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 174, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 228, 229, 262, 263, 331, 428, 429, 430, 431, 440, 445, 446, 447, 449, 450, 451, 462, 464, 468, 516, 517, 518, 519, 520, 521, 522, 523, 525, 526, 527, 528, 529, 530, 532, 533, 534, 535], "summary": {"covered_lines": 86, "num_statements": 86, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 18, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [13, 14, 51, 52, 53, 54, 55, 56, 168, 169, 170, 171, 172, 173, 260, 261, 329, 330], "functions": {"AsyncRecordOperations.__init__": {"executed_lines": [47], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 46}, "AsyncRecordOperations.create": {"executed_lines": [94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 57}, "AsyncRecordOperations.update": {"executed_lines": [155, 156, 157, 158, 159, 160, 161, 162, 163, 164], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 110}, "AsyncRecordOperations.delete": {"executed_lines": [210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 174}, "AsyncRecordOperations.get": {"executed_lines": [428, 429, 430, 431, 440, 445, 446, 447, 449, 464], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 331}, "AsyncRecordOperations.get._paged": {"executed_lines": [450, 451, 462], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 449}, "AsyncRecordOperations.upsert": {"executed_lines": [516, 517, 518, 519, 520, 521, 522, 523, 525, 526, 527, 528, 529, 530, 532, 533, 534, 535], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 468}, "": {"executed_lines": [6, 8, 10, 11, 17, 20, 46, 57, 110, 174, 228, 229, 262, 263, 331, 468], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 12, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [13, 14, 51, 53, 54, 56, 168, 170, 171, 173, 261, 330], "start_line": 1}}, "classes": {"AsyncRecordOperations": {"executed_lines": [47, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 428, 429, 430, 431, 440, 445, 446, 447, 449, 450, 451, 462, 464, 516, 517, 518, 519, 520, 521, 522, 523, 525, 526, 527, 528, 529, 530, 532, 533, 534, 535], "summary": {"covered_lines": 70, "num_statements": 70, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 6, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [52, 55, 169, 172, 260, 329], "start_line": 20}, "": {"executed_lines": [6, 8, 10, 11, 17, 20, 46, 57, 110, 174, 228, 229, 262, 263, 331, 468], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 12, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [13, 14, 51, 53, 54, 56, 168, 170, 171, 173, 261, 330], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\operations\\async_tables.py": {"executed_lines": [6, 8, 10, 16, 17, 18, 19, 25, 28, 64, 65, 69, 134, 135, 142, 146, 163, 164, 168, 188, 189, 190, 191, 192, 196, 244, 245, 249, 277, 278, 282, 310, 311, 315, 373, 374, 379, 389, 429, 430, 434, 443, 462, 463, 467, 486, 487, 488, 489, 490, 494, 559, 560, 571, 575, 626, 627, 628, 629, 638, 660, 661, 662, 666, 690, 691, 695, 735, 736, 740, 769, 770, 774, 814, 815], "summary": {"covered_lines": 75, "num_statements": 75, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [21, 22], "functions": {"AsyncTableOperations.__init__": {"executed_lines": [65], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 64}, "AsyncTableOperations.create": {"executed_lines": [134, 135, 142], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 69}, "AsyncTableOperations.delete": {"executed_lines": [163, 164], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 146}, "AsyncTableOperations.get": {"executed_lines": [188, 189, 190, 191, 192], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 168}, "AsyncTableOperations.list": {"executed_lines": [244, 245], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 196}, "AsyncTableOperations.add_columns": {"executed_lines": [277, 278], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 249}, "AsyncTableOperations.remove_columns": {"executed_lines": [310, 311], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 282}, "AsyncTableOperations.create_one_to_many_relationship": {"executed_lines": [373, 374, 379], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 315}, "AsyncTableOperations.create_many_to_many_relationship": {"executed_lines": [429, 430, 434], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 389}, "AsyncTableOperations.delete_relationship": {"executed_lines": [462, 463], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 443}, "AsyncTableOperations.get_relationship": {"executed_lines": [486, 487, 488, 489, 490], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 467}, "AsyncTableOperations.create_lookup_field": {"executed_lines": [559, 560, 571], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 494}, "AsyncTableOperations.create_alternate_key": {"executed_lines": [626, 627, 628, 629], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 575}, "AsyncTableOperations.get_alternate_keys": {"executed_lines": [660, 661, 662], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 638}, "AsyncTableOperations.delete_alternate_key": {"executed_lines": [690, 691], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 666}, "AsyncTableOperations.list_columns": {"executed_lines": [735, 736], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 695}, "AsyncTableOperations.list_relationships": {"executed_lines": [769, 770], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 740}, "AsyncTableOperations.list_table_relationships": {"executed_lines": [814, 815], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 774}, "": {"executed_lines": [6, 8, 10, 16, 17, 18, 19, 25, 28, 64, 69, 146, 168, 196, 249, 282, 315, 389, 443, 467, 494, 575, 638, 666, 695, 740, 774], "summary": {"covered_lines": 27, "num_statements": 27, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [21, 22], "start_line": 1}}, "classes": {"AsyncTableOperations": {"executed_lines": [65, 134, 135, 142, 163, 164, 188, 189, 190, 191, 192, 244, 245, 277, 278, 310, 311, 373, 374, 379, 429, 430, 434, 462, 463, 486, 487, 488, 489, 490, 559, 560, 571, 626, 627, 628, 629, 660, 661, 662, 690, 691, 735, 736, 769, 770, 814, 815], "summary": {"covered_lines": 48, "num_statements": 48, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 28}, "": {"executed_lines": [6, 8, 10, 16, 17, 18, 19, 25, 28, 64, 69, 146, 168, 196, 249, 282, 315, 389, 443, 467, 494, 575, 638, 666, 695, 740, 774], "summary": {"covered_lines": 27, "num_statements": 27, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [21, 22], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\claude_skill\\__init__.py": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\client.py": {"executed_lines": [4, 6, 7, 8, 10, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 25, 93, 99, 100, 101, 102, 103, 104, 105, 106, 109, 110, 111, 112, 113, 114, 116, 126, 127, 133, 135, 136, 138, 139, 140, 141, 145, 157, 158, 159, 160, 162, 168, 170, 187, 188, 189, 190, 191, 192, 193, 194, 195, 197, 199, 200, 203, 238, 244, 245, 246, 248, 296, 301, 303, 338, 343, 345, 428, 433, 434, 436, 447, 483, 488, 491, 514, 519, 521, 592, 597, 604, 625, 630, 632, 649, 654, 656, 688, 693, 695, 721, 726, 729, 761, 766, 777, 798, 799, 802], "summary": {"covered_lines": 105, "num_statements": 105, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"DataverseClient.__init__": {"executed_lines": [99, 100, 101, 102, 103, 104, 105, 106, 109, 110, 111, 112, 113, 114], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 93}, "DataverseClient._get_odata": {"executed_lines": [126, 127, 133], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 116}, "DataverseClient._scoped_odata": {"executed_lines": [138, 139, 140, 141], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 136}, "DataverseClient.__enter__": {"executed_lines": [157, 158, 159, 160], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 145}, "DataverseClient.__exit__": {"executed_lines": [168], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 162}, "DataverseClient.close": {"executed_lines": [187, 188, 189, 190, 191, 192, 193, 194, 195], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 170}, "DataverseClient._check_closed": {"executed_lines": [199, 200], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 197}, "DataverseClient.create": {"executed_lines": [238, 244, 245, 246], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 203}, "DataverseClient.update": {"executed_lines": [296, 301], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 248}, "DataverseClient.delete": {"executed_lines": [338, 343], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 303}, "DataverseClient.get": {"executed_lines": [428, 433, 434, 436], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 345}, "DataverseClient.query_sql": {"executed_lines": [483, 488], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 447}, "DataverseClient.get_table_info": {"executed_lines": [514, 519], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 491}, "DataverseClient.create_table": {"executed_lines": [592, 597], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 521}, "DataverseClient.delete_table": {"executed_lines": [625, 630], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 604}, "DataverseClient.list_tables": {"executed_lines": [649, 654], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 632}, "DataverseClient.create_columns": {"executed_lines": [688, 693], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 656}, "DataverseClient.delete_columns": {"executed_lines": [721, 726], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 695}, "DataverseClient.upload_file": {"executed_lines": [761, 766], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 729}, "DataverseClient.flush_cache": {"executed_lines": [798, 799], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 777}, "": {"executed_lines": [4, 6, 7, 8, 10, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 25, 93, 116, 135, 136, 145, 162, 170, 197, 203, 248, 303, 345, 447, 491, 521, 604, 632, 656, 695, 729, 777, 802], "summary": {"covered_lines": 38, "num_statements": 38, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"DataverseClient": {"executed_lines": [99, 100, 101, 102, 103, 104, 105, 106, 109, 110, 111, 112, 113, 114, 126, 127, 133, 138, 139, 140, 141, 157, 158, 159, 160, 168, 187, 188, 189, 190, 191, 192, 193, 194, 195, 199, 200, 238, 244, 245, 246, 296, 301, 338, 343, 428, 433, 434, 436, 483, 488, 514, 519, 592, 597, 625, 630, 649, 654, 688, 693, 721, 726, 761, 766, 798, 799], "summary": {"covered_lines": 67, "num_statements": 67, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 25}, "": {"executed_lines": [4, 6, 7, 8, 10, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 25, 93, 116, 135, 136, 145, 162, 170, 197, 203, 248, 303, 345, 447, 491, 521, 604, 632, 656, 695, 729, 777, 802], "summary": {"covered_lines": 38, "num_statements": 38, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\common\\__init__.py": {"executed_lines": [10], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [10], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [10], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\common\\constants.py": {"executed_lines": [12, 13, 14, 15, 16, 21, 22, 24, 25, 27, 28, 30, 31], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [12, 13, 14, 15, 16, 21, 22, 24, 25, 27, 28, 30, 31], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [12, 13, 14, 15, 16, 21, 22, 24, 25, 27, 28, 30, 31], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\core\\__init__.py": {"executed_lines": [11], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [11], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [11], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\core\\_auth.py": {"executed_lines": [12, 14, 16, 19, 20, 30, 31, 34, 43, 44, 45, 46, 48, 58, 59], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"_AuthManager.__init__": {"executed_lines": [44, 45, 46], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 43}, "_AuthManager._acquire_token": {"executed_lines": [58, 59], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 48}, "": {"executed_lines": [12, 14, 16, 19, 20, 30, 31, 34, 43, 48], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_TokenPair": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 20}, "_AuthManager": {"executed_lines": [44, 45, 46, 58, 59], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 34}, "": {"executed_lines": [12, 14, 16, 19, 20, 30, 31, 34, 43, 48], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\core\\_error_codes.py": {"executed_lines": [12, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 28, 44, 45, 46, 47, 48, 49, 50, 51, 54, 57, 58, 59, 60, 61, 62, 63, 66, 81, 84, 93, 96, 108], "summary": {"covered_lines": 36, "num_statements": 36, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"_http_subcode": {"executed_lines": [93], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 84}, "_is_transient_status": {"executed_lines": [108], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 96}, "": {"executed_lines": [12, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 28, 44, 45, 46, 47, 48, 49, 50, 51, 54, 57, 58, 59, 60, 61, 62, 63, 66, 81, 84, 96], "summary": {"covered_lines": 34, "num_statements": 34, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [12, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 28, 44, 45, 46, 47, 48, 49, 50, 51, 54, 57, 58, 59, 60, 61, 62, 63, 66, 81, 84, 93, 96, 108], "summary": {"covered_lines": 36, "num_statements": 36, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\core\\_http.py": {"executed_lines": [12, 14, 15, 17, 23, 45, 53, 54, 55, 56, 57, 59, 77, 78, 79, 81, 82, 86, 87, 88, 89, 92, 93, 101, 102, 103, 104, 105, 106, 108, 111, 112, 120, 121, 122, 123, 130, 131, 132, 133, 134, 136, 141, 142, 143], "summary": {"covered_lines": 45, "num_statements": 46, "percent_covered": 97.82608695652173, "percent_covered_display": "98", "missing_lines": 1, "excluded_lines": 2, "percent_statements_covered": 97.82608695652173, "percent_statements_covered_display": "98"}, "missing_lines": [90], "excluded_lines": [19, 20], "functions": {"_HttpClient.__init__": {"executed_lines": [53, 54, 55, 56, 57], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 45}, "_HttpClient._request": {"executed_lines": [77, 78, 79, 81, 82, 86, 87, 88, 89, 92, 93, 101, 102, 103, 104, 105, 106, 108, 111, 112, 120, 121, 122, 123, 130, 131, 132, 133, 134], "summary": {"covered_lines": 29, "num_statements": 30, "percent_covered": 96.66666666666667, "percent_covered_display": "97", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 96.66666666666667, "percent_statements_covered_display": "97"}, "missing_lines": [90], "excluded_lines": [], "start_line": 59}, "_HttpClient.close": {"executed_lines": [141, 142, 143], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 136}, "": {"executed_lines": [12, 14, 15, 17, 23, 45, 59, 136], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [19, 20], "start_line": 1}}, "classes": {"_HttpClient": {"executed_lines": [53, 54, 55, 56, 57, 77, 78, 79, 81, 82, 86, 87, 88, 89, 92, 93, 101, 102, 103, 104, 105, 106, 108, 111, 112, 120, 121, 122, 123, 130, 131, 132, 133, 134, 141, 142, 143], "summary": {"covered_lines": 37, "num_statements": 38, "percent_covered": 97.36842105263158, "percent_covered_display": "97", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 97.36842105263158, "percent_statements_covered_display": "97"}, "missing_lines": [90], "excluded_lines": [], "start_line": 23}, "": {"executed_lines": [12, 14, 15, 17, 23, 45, 59, 136], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [19, 20], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\core\\_http_logger.py": {"executed_lines": [8, 10, 11, 12, 13, 14, 15, 16, 18, 21, 24, 25, 26, 29, 33, 34, 35, 38, 39, 40, 41, 43, 49, 53, 54, 57, 62, 63, 75, 83, 84, 85, 86, 87, 88, 89, 91, 101, 102, 103, 104, 105, 106, 107, 108, 111, 112, 113, 114, 117, 118, 119, 121, 130, 131, 133, 134, 136, 138, 140, 141, 142, 144, 145, 147, 148, 149, 150, 151, 152, 153, 154, 158, 160, 161, 162, 163, 164, 167, 168, 169], "summary": {"covered_lines": 81, "num_statements": 85, "percent_covered": 95.29411764705883, "percent_covered_display": "95", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 95.29411764705883, "percent_statements_covered_display": "95"}, "missing_lines": [115, 116, 155, 156], "excluded_lines": [], "functions": {"_HttpLogger.__init__": {"executed_lines": [25, 26, 29, 33, 34, 35, 38, 39, 40, 41, 43, 49, 53, 54, 57, 62, 63], "summary": {"covered_lines": 17, "num_statements": 17, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 24}, "_HttpLogger.log_request": {"executed_lines": [83, 84, 85, 86, 87, 88, 89], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 75}, "_HttpLogger.log_response": {"executed_lines": [101, 102, 103, 104, 105, 106, 107, 108, 111, 112, 113, 114, 117, 118, 119], "summary": {"covered_lines": 15, "num_statements": 17, "percent_covered": 88.23529411764706, "percent_covered_display": "88", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 88.23529411764706, "percent_statements_covered_display": "88"}, "missing_lines": [115, 116], "excluded_lines": [], "start_line": 91}, "_HttpLogger.log_error": {"executed_lines": [130, 131], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 121}, "_HttpLogger.body_logging_enabled": {"executed_lines": [136], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 134}, "_HttpLogger.close": {"executed_lines": [140, 141, 142], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 138}, "_HttpLogger._redact_headers": {"executed_lines": [145], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 144}, "_HttpLogger._truncate_body": {"executed_lines": [148, 149, 150, 151, 152, 153, 154, 158, 160, 161, 162, 163, 164, 167, 168, 169], "summary": {"covered_lines": 16, "num_statements": 18, "percent_covered": 88.88888888888889, "percent_covered_display": "89", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 88.88888888888889, "percent_statements_covered_display": "89"}, "missing_lines": [155, 156], "excluded_lines": [], "start_line": 147}, "": {"executed_lines": [8, 10, 11, 12, 13, 14, 15, 16, 18, 21, 24, 75, 91, 121, 133, 134, 138, 144, 147], "summary": {"covered_lines": 19, "num_statements": 19, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_HttpLogger": {"executed_lines": [25, 26, 29, 33, 34, 35, 38, 39, 40, 41, 43, 49, 53, 54, 57, 62, 63, 83, 84, 85, 86, 87, 88, 89, 101, 102, 103, 104, 105, 106, 107, 108, 111, 112, 113, 114, 117, 118, 119, 130, 131, 136, 140, 141, 142, 145, 148, 149, 150, 151, 152, 153, 154, 158, 160, 161, 162, 163, 164, 167, 168, 169], "summary": {"covered_lines": 62, "num_statements": 66, "percent_covered": 93.93939393939394, "percent_covered_display": "94", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 93.93939393939394, "percent_statements_covered_display": "94"}, "missing_lines": [115, 116, 155, 156], "excluded_lines": [], "start_line": 21}, "": {"executed_lines": [8, 10, 11, 12, 13, 14, 15, 16, 18, 21, 24, 75, 91, 121, 133, 134, 138, 144, 147], "summary": {"covered_lines": 19, "num_statements": 19, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\core\\config.py": {"executed_lines": [12, 14, 15, 21, 22, 40, 43, 44, 45, 47, 49, 50, 58], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [17, 18], "functions": {"DataverseConfig.from_env": {"executed_lines": [58], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 50}, "": {"executed_lines": [12, 14, 15, 21, 22, 40, 43, 44, 45, 47, 49, 50], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [17, 18], "start_line": 1}}, "classes": {"DataverseConfig": {"executed_lines": [58], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 22}, "": {"executed_lines": [12, 14, 15, 21, 22, 40, 43, 44, 45, 47, 49, 50], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [17, 18], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\core\\errors.py": {"executed_lines": [15, 16, 17, 20, 40, 50, 51, 52, 53, 54, 55, 56, 57, 58, 60, 67, 82, 94, 95, 98, 110, 111, 114, 126, 127, 130, 160, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 201], "summary": {"covered_lines": 44, "num_statements": 44, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [78, 79], "functions": {"DataverseError.__init__": {"executed_lines": [50, 51, 52, 53, 54, 55, 56, 57, 58], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 40}, "DataverseError.to_dict": {"executed_lines": [67], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 60}, "DataverseError.__repr__": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 1, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [79], "start_line": 78}, "ValidationError.__init__": {"executed_lines": [95], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 94}, "MetadataError.__init__": {"executed_lines": [111], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 110}, "SQLParseError.__init__": {"executed_lines": [127], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 126}, "HttpError.__init__": {"executed_lines": [175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 160}, "": {"executed_lines": [15, 16, 17, 20, 40, 60, 82, 94, 98, 110, 114, 126, 130, 160, 201], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 1, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [78], "start_line": 1}}, "classes": {"DataverseError": {"executed_lines": [50, 51, 52, 53, 54, 55, 56, 57, 58, 67], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 1, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [79], "start_line": 20}, "ValidationError": {"executed_lines": [95], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 82}, "MetadataError": {"executed_lines": [111], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 98}, "SQLParseError": {"executed_lines": [127], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 114}, "HttpError": {"executed_lines": [175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 130}, "": {"executed_lines": [15, 16, 17, 20, 40, 60, 82, 94, 98, 110, 114, 126, 130, 160, 201], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 1, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [78], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\core\\log_config.py": {"executed_lines": [11, 13, 14, 16, 18, 21, 31, 32, 35, 36, 61, 62, 63, 67, 68, 69, 72, 73, 74, 75], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"_default_redacted_headers": {"executed_lines": [32], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 31}, "LogConfig.__post_init__": {"executed_lines": [62, 63], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 61}, "": {"executed_lines": [11, 13, 14, 16, 18, 21, 31, 35, 36, 61, 67, 68, 69, 72, 73, 74, 75], "summary": {"covered_lines": 17, "num_statements": 17, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"LogConfig": {"executed_lines": [62, 63], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 36}, "": {"executed_lines": [11, 13, 14, 16, 18, 21, 31, 32, 35, 36, 61, 67, 68, 69, 72, 73, 74, 75], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\data\\__init__.py": {"executed_lines": [11], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [11], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [11], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\data\\_batch.py": {"executed_lines": [6, 8, 9, 11, 12, 13, 14, 15, 42, 50, 62, 73, 74, 76, 78, 79, 80, 87, 88, 90, 93, 94, 96, 97, 108, 114, 115, 116, 117, 118, 120, 121, 122, 124, 125, 127, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 168, 170, 171, 172, 176, 182, 183, 184, 185, 186, 188, 189, 190, 191, 192, 193, 194, 196, 197, 198, 199, 200, 201, 202, 203, 204, 206, 207, 209, 210, 211, 212, 213, 214, 215, 216, 224, 226, 227, 228, 232, 234, 235, 236, 238, 239, 240, 242, 243, 244, 245, 246, 247, 250, 251, 255, 256, 262, 263], "summary": {"covered_lines": 130, "num_statements": 130, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [39, 40], "functions": {"_BatchClient.execute": {"executed_lines": [73, 74, 76, 78, 79, 80, 87, 88, 90, 93, 94, 96, 97, 108], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 62}, "_BatchClient._resolve_all": {"executed_lines": [115, 116, 117, 118, 120, 121, 122, 124, 125], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 114}, "_BatchClient._resolve_item": {"executed_lines": [129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163], "summary": {"covered_lines": 35, "num_statements": 35, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 127}, "_BatchClient._resolve_one": {"executed_lines": [170, 171, 172, 176], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 168}, "_BatchClient._resolve_record_create": {"executed_lines": [183, 184, 185, 186], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 182}, "_BatchClient._resolve_record_update": {"executed_lines": [189, 190, 191, 192, 193, 194], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 188}, "_BatchClient._resolve_record_delete": {"executed_lines": [197, 198, 199, 200, 201, 202, 203, 204], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 196}, "_BatchClient._resolve_record_get": {"executed_lines": [207], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 206}, "_BatchClient._resolve_record_upsert": {"executed_lines": [210, 211, 212, 213, 214, 215, 216], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 209}, "_BatchClient._require_entity_metadata": {"executed_lines": [226, 227, 228, 232], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 224}, "_BatchClient._resolve_table_delete": {"executed_lines": [235, 236], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 234}, "_BatchClient._resolve_table_add_columns": {"executed_lines": [239, 240], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 238}, "_BatchClient._resolve_table_remove_columns": {"executed_lines": [243, 244, 245, 246, 247, 250, 251, 255, 256], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 242}, "_BatchClient._resolve_query_sql": {"executed_lines": [263], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 262}, "": {"executed_lines": [6, 8, 9, 11, 12, 13, 14, 15, 42, 50, 62, 114, 127, 168, 182, 188, 196, 206, 209, 224, 234, 238, 242, 262], "summary": {"covered_lines": 24, "num_statements": 24, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [39, 40], "start_line": 1}}, "classes": {"_BatchClient": {"executed_lines": [73, 74, 76, 78, 79, 80, 87, 88, 90, 93, 94, 96, 97, 108, 115, 116, 117, 118, 120, 121, 122, 124, 125, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 170, 171, 172, 176, 183, 184, 185, 186, 189, 190, 191, 192, 193, 194, 197, 198, 199, 200, 201, 202, 203, 204, 207, 210, 211, 212, 213, 214, 215, 216, 226, 227, 228, 232, 235, 236, 239, 240, 243, 244, 245, 246, 247, 250, 251, 255, 256, 263], "summary": {"covered_lines": 106, "num_statements": 106, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 50}, "": {"executed_lines": [6, 8, 9, 11, 12, 13, 14, 15, 42, 50, 62, 114, 127, 168, 182, 188, 196, 206, 209, 224, 234, 238, 242, 262], "summary": {"covered_lines": 24, "num_statements": 24, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [39, 40], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\data\\_batch_base.py": {"executed_lines": [9, 11, 12, 13, 14, 15, 17, 18, 19, 20, 25, 26, 27, 28, 33, 35, 36, 47, 48, 49, 50, 51, 54, 55, 56, 57, 58, 59, 62, 63, 64, 65, 66, 67, 70, 71, 72, 73, 74, 77, 78, 79, 80, 86, 87, 88, 89, 90, 91, 92, 95, 96, 97, 100, 101, 102, 105, 106, 107, 108, 111, 112, 113, 114, 117, 118, 119, 120, 123, 124, 125, 126, 127, 130, 131, 132, 133, 136, 137, 138, 141, 142, 143, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 162, 163, 164, 172, 173, 186, 187, 189, 191, 192, 193, 194, 196, 198, 199, 200, 202, 204, 205, 206, 216, 217, 220, 228, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 253, 256, 257, 258, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 283, 284, 285, 287, 288, 289, 290, 291, 292, 293, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 344, 345, 346, 347, 362, 372, 373, 379, 380, 382, 383, 385, 386, 388, 389, 390, 391, 393, 394, 396, 397, 399, 400, 402, 403, 413, 414, 415, 421, 426, 427, 428, 429, 431, 432, 434, 436, 441, 442, 444, 445, 446, 447, 448, 449, 450, 451, 452, 454, 455, 456, 458, 459, 460, 461, 462, 464, 467, 473, 474, 475, 476, 481, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 495, 496, 497, 498], "summary": {"covered_lines": 287, "num_statements": 288, "percent_covered": 99.65277777777777, "percent_covered_display": "99", "missing_lines": 1, "excluded_lines": 2, "percent_statements_covered": 99.65277777777777, "percent_statements_covered_display": "99"}, "missing_lines": [482], "excluded_lines": [30, 31], "functions": {"_ChangeSet.add_create": {"executed_lines": [191, 192, 193, 194], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 189}, "_ChangeSet.add_update": {"executed_lines": [198, 199, 200], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 196}, "_ChangeSet.add_delete": {"executed_lines": [204, 205, 206], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 202}, "_raise_top_level_batch_error": {"executed_lines": [236, 237, 238, 239, 240, 241, 242, 243, 244, 245], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 228}, "_extract_boundary": {"executed_lines": [257, 258], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 256}, "_split_multipart": {"executed_lines": [262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280], "summary": {"covered_lines": 19, "num_statements": 19, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 261}, "_parse_mime_part": {"executed_lines": [284, 285, 287, 288, 289, 290, 291, 292, 293], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 283}, "_parse_http_response_part": {"executed_lines": [297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 344, 345, 346, 347], "summary": {"covered_lines": 50, "num_statements": 50, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 296}, "_BatchBase.__init__": {"executed_lines": [373], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 372}, "_BatchBase._resolve_table_create": {"executed_lines": [380], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 379}, "_BatchBase._resolve_table_get": {"executed_lines": [383], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 382}, "_BatchBase._resolve_table_list": {"executed_lines": [386], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 385}, "_BatchBase._resolve_table_create_one_to_many": {"executed_lines": [389, 390, 391], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 388}, "_BatchBase._resolve_table_create_many_to_many": {"executed_lines": [394], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 393}, "_BatchBase._resolve_table_delete_relationship": {"executed_lines": [397], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 396}, "_BatchBase._resolve_table_get_relationship": {"executed_lines": [400], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 399}, "_BatchBase._resolve_table_create_lookup_field": {"executed_lines": [403, 413, 414, 415], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 402}, "_BatchBase._build_batch_body": {"executed_lines": [426, 427, 428, 429, 431, 432], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 421}, "_BatchBase._serialize_raw_request": {"executed_lines": [436, 441, 442, 444, 445, 446, 447, 448, 449, 450, 451, 452, 454, 455, 456], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 434}, "_BatchBase._serialize_changeset_item": {"executed_lines": [459, 460, 461, 462, 464, 467], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 458}, "_BatchBase._parse_batch_response": {"executed_lines": [474, 475, 476, 481, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 495, 496, 497, 498], "summary": {"covered_lines": 19, "num_statements": 20, "percent_covered": 95.0, "percent_covered_display": "95", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 95.0, "percent_statements_covered_display": "95"}, "missing_lines": [482], "excluded_lines": [], "start_line": 473}, "": {"executed_lines": [9, 11, 12, 13, 14, 15, 17, 18, 19, 20, 25, 26, 27, 28, 33, 35, 36, 47, 48, 49, 50, 51, 54, 55, 56, 57, 58, 59, 62, 63, 64, 65, 66, 67, 70, 71, 72, 73, 74, 77, 78, 79, 80, 86, 87, 88, 89, 90, 91, 92, 95, 96, 97, 100, 101, 102, 105, 106, 107, 108, 111, 112, 113, 114, 117, 118, 119, 120, 123, 124, 125, 126, 127, 130, 131, 132, 133, 136, 137, 138, 141, 142, 143, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 162, 163, 164, 172, 173, 186, 187, 189, 196, 202, 216, 217, 220, 228, 253, 256, 261, 283, 296, 362, 372, 379, 382, 385, 388, 393, 396, 399, 402, 421, 434, 458, 473], "summary": {"covered_lines": 127, "num_statements": 127, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [30, 31], "start_line": 1}}, "classes": {"_RecordCreate": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 48}, "_RecordUpdate": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 55}, "_RecordDelete": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 63}, "_RecordGet": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 71}, "_RecordUpsert": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 78}, "_TableCreate": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 87}, "_TableDelete": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 96}, "_TableGet": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 101}, "_TableList": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 106}, "_TableAddColumns": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 112}, "_TableRemoveColumns": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 118}, "_TableCreateOneToMany": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 124}, "_TableCreateManyToMany": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 131}, "_TableDeleteRelationship": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 137}, "_TableGetRelationship": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 142}, "_TableCreateLookupField": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 147}, "_QuerySql": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 163}, "_ChangeSet": {"executed_lines": [191, 192, 193, 194, 198, 199, 200, 204, 205, 206], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 173}, "_ChangeSetBatchItem": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 217}, "_BatchBase": {"executed_lines": [373, 380, 383, 386, 389, 390, 391, 394, 397, 400, 403, 413, 414, 415, 426, 427, 428, 429, 431, 432, 436, 441, 442, 444, 445, 446, 447, 448, 449, 450, 451, 452, 454, 455, 456, 459, 460, 461, 462, 464, 467, 474, 475, 476, 481, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 495, 496, 497, 498], "summary": {"covered_lines": 60, "num_statements": 61, "percent_covered": 98.36065573770492, "percent_covered_display": "98", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 98.36065573770492, "percent_statements_covered_display": "98"}, "missing_lines": [482], "excluded_lines": [], "start_line": 362}, "": {"executed_lines": [9, 11, 12, 13, 14, 15, 17, 18, 19, 20, 25, 26, 27, 28, 33, 35, 36, 47, 48, 49, 50, 51, 54, 55, 56, 57, 58, 59, 62, 63, 64, 65, 66, 67, 70, 71, 72, 73, 74, 77, 78, 79, 80, 86, 87, 88, 89, 90, 91, 92, 95, 96, 97, 100, 101, 102, 105, 106, 107, 108, 111, 112, 113, 114, 117, 118, 119, 120, 123, 124, 125, 126, 127, 130, 131, 132, 133, 136, 137, 138, 141, 142, 143, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 162, 163, 164, 172, 173, 186, 187, 189, 196, 202, 216, 217, 220, 228, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 253, 256, 257, 258, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 283, 284, 285, 287, 288, 289, 290, 291, 292, 293, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 344, 345, 346, 347, 362, 372, 379, 382, 385, 388, 393, 396, 399, 402, 421, 434, 458, 473], "summary": {"covered_lines": 217, "num_statements": 217, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [30, 31], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\data\\_odata.py": {"executed_lines": [6, 8, 10, 11, 12, 13, 14, 16, 18, 19, 20, 21, 22, 23, 36, 46, 49, 70, 71, 72, 80, 86, 87, 88, 90, 92, 93, 94, 103, 104, 105, 106, 107, 108, 109, 111, 112, 114, 115, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 140, 141, 142, 143, 144, 145, 146, 147, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 179, 185, 186, 187, 188, 189, 190, 193, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 225, 241, 242, 243, 244, 245, 246, 247, 248, 249, 251, 252, 253, 255, 256, 258, 259, 260, 262, 263, 264, 265, 266, 267, 269, 295, 296, 297, 298, 299, 301, 332, 333, 336, 337, 338, 339, 340, 341, 342, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 357, 359, 360, 361, 362, 364, 365, 366, 367, 368, 372, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 409, 424, 425, 426, 427, 431, 432, 433, 434, 435, 436, 437, 438, 440, 452, 454, 472, 473, 474, 475, 477, 488, 490, 503, 505, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 553, 554, 555, 556, 557, 558, 559, 561, 562, 563, 564, 566, 567, 569, 570, 572, 573, 575, 576, 577, 578, 579, 581, 582, 583, 584, 586, 587, 588, 590, 591, 592, 593, 594, 595, 598, 617, 618, 619, 620, 621, 626, 628, 629, 630, 631, 632, 635, 636, 637, 638, 639, 641, 642, 643, 646, 647, 648, 649, 650, 652, 653, 662, 663, 667, 668, 669, 670, 679, 680, 681, 682, 683, 684, 691, 692, 693, 694, 695, 702, 703, 704, 705, 706, 707, 708, 709, 710, 712, 715, 720, 721, 724, 725, 726, 727, 728, 730, 731, 732, 736, 737, 738, 739, 740, 741, 742, 743, 748, 752, 753, 754, 755, 759, 760, 761, 762, 763, 766, 777, 779, 780, 781, 785, 786, 787, 789, 796, 797, 809, 810, 811, 812, 813, 817, 818, 821, 822, 823, 825, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843, 844, 845, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859, 861, 889, 890, 891, 895, 896, 897, 898, 899, 900, 901, 902, 903, 905, 918, 919, 920, 921, 923, 924, 925, 926, 927, 928, 929, 930, 931, 934, 939, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 954, 962, 963, 964, 965, 966, 968, 969, 974, 975, 976, 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, 990, 992, 993, 995, 996, 997, 998, 999, 1000, 1001, 1002, 1003, 1004, 1006, 1008, 1017, 1020, 1024, 1025, 1028, 1031, 1032, 1033, 1035, 1037, 1038, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1049, 1050, 1052, 1061, 1062, 1063, 1064, 1074, 1101, 1103, 1115, 1116, 1117, 1121, 1125, 1152, 1153, 1154, 1159, 1160, 1161, 1165, 1166, 1167, 1168, 1170, 1176, 1190, 1191, 1192, 1197, 1198, 1199, 1200, 1202, 1218, 1219, 1220, 1225, 1226, 1227, 1229, 1259, 1260, 1261, 1266, 1270, 1271, 1273, 1277, 1278, 1279, 1280, 1281, 1282, 1283, 1284, 1286, 1287, 1288, 1289, 1290, 1292, 1293, 1294, 1296, 1303, 1313, 1333, 1334, 1336, 1337, 1338, 1343, 1344, 1345, 1347, 1348, 1349, 1350, 1354, 1355, 1356, 1361, 1362, 1364, 1365, 1367, 1369, 1390, 1391, 1392, 1393, 1395, 1397, 1398, 1399, 1401, 1402, 1403, 1409, 1410, 1411, 1412, 1414, 1415, 1416, 1417, 1422, 1423, 1424, 1426, 1428, 1429, 1430, 1431, 1432, 1434, 1436, 1437, 1439, 1443, 1452, 1453, 1454, 1461, 1468, 1470, 1471, 1472, 1473, 1474, 1475, 1476, 1477, 1478, 1484, 1497, 1498, 1499, 1502, 1503, 1504, 1512, 1524, 1525, 1526, 1527, 1528, 1529, 1530, 1531, 1532, 1538, 1560, 1582, 1590, 1591, 1595, 1596, 1597, 1598, 1599, 1600, 1601, 1604, 1605, 1609, 1610, 1611, 1612, 1613, 1614, 1620, 1631, 1634, 1635, 1636, 1643, 1645, 1646, 1647, 1648, 1679, 1685, 1693, 1694, 1695, 1696, 1697, 1699, 1714, 1715, 1716], "summary": {"covered_lines": 670, "num_statements": 690, "percent_covered": 97.10144927536231, "percent_covered_display": "97", "missing_lines": 20, "excluded_lines": 0, "percent_statements_covered": 97.10144927536231, "percent_statements_covered_display": "97"}, "missing_lines": [991, 994, 1034, 1469, 1500, 1546, 1547, 1548, 1549, 1550, 1551, 1555, 1557, 1558, 1572, 1573, 1574, 1575, 1576, 1632], "excluded_lines": [], "functions": {"_ODataClient.__init__": {"executed_lines": [70, 71, 72], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 49}, "_ODataClient.close": {"executed_lines": [86, 87, 88], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 80}, "_ODataClient._headers": {"executed_lines": [92, 93, 94], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 90}, "_ODataClient._merge_headers": {"executed_lines": [104, 105, 106, 107, 108, 109], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 103}, "_ODataClient._raw_request": {"executed_lines": [112], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 111}, "_ODataClient._request": {"executed_lines": [115, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 140, 141, 142, 143, 144, 145, 146, 147, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161], "summary": {"covered_lines": 35, "num_statements": 35, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 114}, "_ODataClient._execute_raw": {"executed_lines": [185, 186, 187, 188, 189, 190], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 179}, "_ODataClient._create": {"executed_lines": [209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 193}, "_ODataClient._create_multiple": {"executed_lines": [241, 242, 243, 244, 245, 246, 247, 248, 249, 251, 252, 253, 255, 256, 258, 259, 260, 262, 263, 264, 265, 266, 267], "summary": {"covered_lines": 23, "num_statements": 23, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 225}, "_ODataClient._upsert": {"executed_lines": [295, 296, 297, 298, 299], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 269}, "_ODataClient._upsert_multiple": {"executed_lines": [332, 333, 336, 337, 338, 339, 340, 341, 342, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354], "summary": {"covered_lines": 19, "num_statements": 19, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 301}, "_ODataClient._primary_id_attr": {"executed_lines": [359, 360, 361, 362, 364, 365, 366, 367, 368], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 357}, "_ODataClient._update_by_ids": {"executed_lines": [387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407], "summary": {"covered_lines": 21, "num_statements": 21, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 372}, "_ODataClient._delete_multiple": {"executed_lines": [424, 425, 426, 427, 431, 432, 433, 434, 435, 436, 437, 438], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 409}, "_ODataClient._update": {"executed_lines": [452], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 440}, "_ODataClient._update_multiple": {"executed_lines": [472, 473, 474, 475], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 454}, "_ODataClient._delete": {"executed_lines": [488], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 477}, "_ODataClient._get": {"executed_lines": [503], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 490}, "_ODataClient._get_multiple": {"executed_lines": [542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 553, 561, 562, 563, 564, 566, 567, 569, 570, 572, 573, 575, 576, 577, 578, 579, 581, 582, 583, 584, 586, 587, 588, 590, 591, 592, 593, 594, 595], "summary": {"covered_lines": 39, "num_statements": 39, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 505}, "_ODataClient._get_multiple._do_request": {"executed_lines": [554, 555, 556, 557, 558, 559], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 553}, "_ODataClient._query_sql": {"executed_lines": [617, 618, 619, 620, 621, 626, 628, 629, 630, 631, 632, 635, 636, 637, 638, 639, 641, 642, 643, 646, 647, 648, 649, 650, 652, 653, 662, 663, 667, 668, 669, 670, 679, 680, 681, 682, 683, 684, 691, 692, 693, 694, 695, 702, 703, 704, 705, 706, 707, 708, 709, 710, 712], "summary": {"covered_lines": 53, "num_statements": 53, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 598}, "_ODataClient._entity_set_from_schema_name": {"executed_lines": [720, 721, 724, 725, 726, 727, 728, 730, 731, 732, 736, 737, 738, 739, 740, 741, 742, 743, 748, 752, 753, 754, 755, 759, 760, 761, 762, 763], "summary": {"covered_lines": 28, "num_statements": 28, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 715}, "_ODataClient._get_entity_by_table_schema_name": {"executed_lines": [777, 779, 780, 781, 785, 786, 787], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 766}, "_ODataClient._create_entity": {"executed_lines": [796, 797, 809, 810, 811, 812, 813, 817, 818, 821, 822, 823], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 789}, "_ODataClient._get_attribute_metadata": {"executed_lines": [832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843, 844, 845, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859], "summary": {"covered_lines": 25, "num_statements": 25, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 825}, "_ODataClient._list_columns": {"executed_lines": [889, 890, 891, 895, 896, 897, 898, 899, 900, 901, 902, 903], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 861}, "_ODataClient._wait_for_attribute_visibility": {"executed_lines": [918, 919, 920, 921, 923, 924, 925, 926, 927, 928, 929, 930, 931, 934], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 905}, "_ODataClient._request_metadata_with_retry": {"executed_lines": [941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 939}, "_ODataClient._bulk_fetch_picklists": {"executed_lines": [962, 963, 964, 965, 966, 968, 969, 974, 975, 976, 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, 990, 992, 993, 995, 996, 997, 998, 999, 1000, 1001, 1002, 1003, 1004, 1006], "summary": {"covered_lines": 36, "num_statements": 38, "percent_covered": 94.73684210526316, "percent_covered_display": "95", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 94.73684210526316, "percent_statements_covered_display": "95"}, "missing_lines": [991, 994], "excluded_lines": [], "start_line": 954}, "_ODataClient._convert_labels_to_ints": {"executed_lines": [1017, 1020, 1024, 1025, 1028, 1031, 1032, 1033, 1035, 1037, 1038, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1049, 1050], "summary": {"covered_lines": 23, "num_statements": 24, "percent_covered": 95.83333333333333, "percent_covered_display": "96", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 95.83333333333333, "percent_statements_covered_display": "96"}, "missing_lines": [1034], "excluded_lines": [], "start_line": 1008}, "_ODataClient._get_table_info": {"executed_lines": [1061, 1062, 1063, 1064], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1052}, "_ODataClient._list_tables": {"executed_lines": [1101], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1074}, "_ODataClient._delete_table": {"executed_lines": [1115, 1116, 1117, 1121], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1103}, "_ODataClient._create_alternate_key": {"executed_lines": [1152, 1153, 1154, 1159, 1160, 1161, 1165, 1166, 1167, 1168, 1170], "summary": {"covered_lines": 11, "num_statements": 11, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1125}, "_ODataClient._get_alternate_keys": {"executed_lines": [1190, 1191, 1192, 1197, 1198, 1199, 1200], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1176}, "_ODataClient._delete_alternate_key": {"executed_lines": [1218, 1219, 1220, 1225, 1226, 1227], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1202}, "_ODataClient._create_table": {"executed_lines": [1259, 1260, 1261, 1266, 1270, 1271, 1273, 1277, 1278, 1279, 1280, 1281, 1282, 1283, 1284, 1286, 1287, 1288, 1289, 1290, 1292, 1293, 1294, 1296, 1303], "summary": {"covered_lines": 25, "num_statements": 25, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1229}, "_ODataClient._create_columns": {"executed_lines": [1333, 1334, 1336, 1337, 1338, 1343, 1344, 1345, 1347, 1348, 1349, 1350, 1354, 1355, 1356, 1361, 1362, 1364, 1365, 1367], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1313}, "_ODataClient._delete_columns": {"executed_lines": [1390, 1391, 1392, 1393, 1395, 1397, 1398, 1399, 1401, 1402, 1403, 1409, 1410, 1411, 1412, 1414, 1415, 1416, 1417, 1422, 1423, 1424, 1426, 1428, 1429, 1430, 1431, 1432, 1434, 1436, 1437, 1439], "summary": {"covered_lines": 32, "num_statements": 32, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1369}, "_ODataClient._build_create": {"executed_lines": [1452, 1453, 1454], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1443}, "_ODataClient._build_create_multiple": {"executed_lines": [1468, 1470, 1471, 1472, 1473, 1474, 1475, 1476, 1477, 1478], "summary": {"covered_lines": 10, "num_statements": 11, "percent_covered": 90.9090909090909, "percent_covered_display": "91", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 90.9090909090909, "percent_statements_covered_display": "91"}, "missing_lines": [1469], "excluded_lines": [], "start_line": 1461}, "_ODataClient._build_update": {"executed_lines": [1497, 1498, 1499, 1502, 1503, 1504], "summary": {"covered_lines": 6, "num_statements": 7, "percent_covered": 85.71428571428571, "percent_covered_display": "86", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 85.71428571428571, "percent_statements_covered_display": "86"}, "missing_lines": [1500], "excluded_lines": [], "start_line": 1484}, "_ODataClient._build_update_multiple_from_records": {"executed_lines": [1524, 1525, 1526, 1527, 1528, 1529, 1530, 1531, 1532], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1512}, "_ODataClient._build_update_multiple": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 9, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 9, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1546, 1547, 1548, 1549, 1550, 1551, 1555, 1557, 1558], "excluded_lines": [], "start_line": 1538}, "_ODataClient._build_upsert": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 5, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 5, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1572, 1573, 1574, 1575, 1576], "excluded_lines": [], "start_line": 1560}, "_ODataClient._build_upsert_multiple": {"executed_lines": [1590, 1591, 1595, 1596, 1597, 1598, 1599, 1600, 1601, 1604, 1605, 1609, 1610, 1611, 1612, 1613, 1614], "summary": {"covered_lines": 17, "num_statements": 17, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1582}, "_ODataClient._build_delete": {"executed_lines": [1631, 1634, 1635, 1636], "summary": {"covered_lines": 4, "num_statements": 5, "percent_covered": 80.0, "percent_covered_display": "80", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 80.0, "percent_statements_covered_display": "80"}, "missing_lines": [1632], "excluded_lines": [], "start_line": 1620}, "_ODataClient._build_delete_multiple": {"executed_lines": [1645, 1646, 1647, 1648, 1679], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1643}, "_ODataClient._build_get": {"executed_lines": [1693, 1694, 1695, 1696, 1697], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1685}, "_ODataClient._build_sql": {"executed_lines": [1714, 1715, 1716], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1699}, "": {"executed_lines": [6, 8, 10, 11, 12, 13, 14, 16, 18, 19, 20, 21, 22, 23, 36, 46, 49, 80, 90, 103, 111, 114, 179, 193, 225, 269, 301, 357, 372, 409, 440, 454, 477, 490, 505, 598, 715, 766, 789, 825, 861, 905, 939, 954, 1008, 1052, 1074, 1103, 1125, 1176, 1202, 1229, 1313, 1369, 1443, 1461, 1484, 1512, 1538, 1560, 1582, 1620, 1643, 1685, 1699], "summary": {"covered_lines": 65, "num_statements": 65, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_ODataClient": {"executed_lines": [70, 71, 72, 86, 87, 88, 92, 93, 94, 104, 105, 106, 107, 108, 109, 112, 115, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 140, 141, 142, 143, 144, 145, 146, 147, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 185, 186, 187, 188, 189, 190, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 241, 242, 243, 244, 245, 246, 247, 248, 249, 251, 252, 253, 255, 256, 258, 259, 260, 262, 263, 264, 265, 266, 267, 295, 296, 297, 298, 299, 332, 333, 336, 337, 338, 339, 340, 341, 342, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 359, 360, 361, 362, 364, 365, 366, 367, 368, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 424, 425, 426, 427, 431, 432, 433, 434, 435, 436, 437, 438, 452, 472, 473, 474, 475, 488, 503, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 553, 554, 555, 556, 557, 558, 559, 561, 562, 563, 564, 566, 567, 569, 570, 572, 573, 575, 576, 577, 578, 579, 581, 582, 583, 584, 586, 587, 588, 590, 591, 592, 593, 594, 595, 617, 618, 619, 620, 621, 626, 628, 629, 630, 631, 632, 635, 636, 637, 638, 639, 641, 642, 643, 646, 647, 648, 649, 650, 652, 653, 662, 663, 667, 668, 669, 670, 679, 680, 681, 682, 683, 684, 691, 692, 693, 694, 695, 702, 703, 704, 705, 706, 707, 708, 709, 710, 712, 720, 721, 724, 725, 726, 727, 728, 730, 731, 732, 736, 737, 738, 739, 740, 741, 742, 743, 748, 752, 753, 754, 755, 759, 760, 761, 762, 763, 777, 779, 780, 781, 785, 786, 787, 796, 797, 809, 810, 811, 812, 813, 817, 818, 821, 822, 823, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843, 844, 845, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859, 889, 890, 891, 895, 896, 897, 898, 899, 900, 901, 902, 903, 918, 919, 920, 921, 923, 924, 925, 926, 927, 928, 929, 930, 931, 934, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 962, 963, 964, 965, 966, 968, 969, 974, 975, 976, 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, 990, 992, 993, 995, 996, 997, 998, 999, 1000, 1001, 1002, 1003, 1004, 1006, 1017, 1020, 1024, 1025, 1028, 1031, 1032, 1033, 1035, 1037, 1038, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1049, 1050, 1061, 1062, 1063, 1064, 1101, 1115, 1116, 1117, 1121, 1152, 1153, 1154, 1159, 1160, 1161, 1165, 1166, 1167, 1168, 1170, 1190, 1191, 1192, 1197, 1198, 1199, 1200, 1218, 1219, 1220, 1225, 1226, 1227, 1259, 1260, 1261, 1266, 1270, 1271, 1273, 1277, 1278, 1279, 1280, 1281, 1282, 1283, 1284, 1286, 1287, 1288, 1289, 1290, 1292, 1293, 1294, 1296, 1303, 1333, 1334, 1336, 1337, 1338, 1343, 1344, 1345, 1347, 1348, 1349, 1350, 1354, 1355, 1356, 1361, 1362, 1364, 1365, 1367, 1390, 1391, 1392, 1393, 1395, 1397, 1398, 1399, 1401, 1402, 1403, 1409, 1410, 1411, 1412, 1414, 1415, 1416, 1417, 1422, 1423, 1424, 1426, 1428, 1429, 1430, 1431, 1432, 1434, 1436, 1437, 1439, 1452, 1453, 1454, 1468, 1470, 1471, 1472, 1473, 1474, 1475, 1476, 1477, 1478, 1497, 1498, 1499, 1502, 1503, 1504, 1524, 1525, 1526, 1527, 1528, 1529, 1530, 1531, 1532, 1590, 1591, 1595, 1596, 1597, 1598, 1599, 1600, 1601, 1604, 1605, 1609, 1610, 1611, 1612, 1613, 1614, 1631, 1634, 1635, 1636, 1645, 1646, 1647, 1648, 1679, 1693, 1694, 1695, 1696, 1697, 1714, 1715, 1716], "summary": {"covered_lines": 605, "num_statements": 625, "percent_covered": 96.8, "percent_covered_display": "97", "missing_lines": 20, "excluded_lines": 0, "percent_statements_covered": 96.8, "percent_statements_covered_display": "97"}, "missing_lines": [991, 994, 1034, 1469, 1500, 1546, 1547, 1548, 1549, 1550, 1551, 1555, 1557, 1558, 1572, 1573, 1574, 1575, 1576, 1632], "excluded_lines": [], "start_line": 46}, "": {"executed_lines": [6, 8, 10, 11, 12, 13, 14, 16, 18, 19, 20, 21, 22, 23, 36, 46, 49, 80, 90, 103, 111, 114, 179, 193, 225, 269, 301, 357, 372, 409, 440, 454, 477, 490, 505, 598, 715, 766, 789, 825, 861, 905, 939, 954, 1008, 1052, 1074, 1103, 1125, 1176, 1202, 1229, 1313, 1369, 1443, 1461, 1484, 1512, 1538, 1560, 1582, 1620, 1643, 1685, 1699], "summary": {"covered_lines": 65, "num_statements": 65, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\data\\_odata_base.py": {"executed_lines": [10, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24, 26, 27, 33, 38, 39, 40, 42, 44, 45, 46, 47, 50, 60, 61, 62, 63, 64, 70, 71, 72, 73, 74, 75, 78, 79, 82, 83, 84, 85, 86, 88, 89, 98, 99, 100, 101, 102, 103, 112, 120, 130, 131, 133, 134, 140, 142, 143, 144, 145, 146, 155, 156, 158, 160, 161, 163, 165, 166, 179, 180, 181, 183, 184, 189, 190, 191, 193, 194, 202, 203, 205, 206, 207, 208, 209, 210, 216, 217, 219, 220, 221, 222, 224, 226, 227, 228, 229, 231, 233, 235, 237, 238, 239, 240, 241, 243, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 270, 271, 273, 274, 275, 286, 287, 288, 290, 292, 295, 296, 298, 299, 305, 310, 311, 312, 313, 314, 315, 316, 323, 324, 325, 330, 343, 344, 345, 348, 349, 350, 352, 353, 356, 358, 360, 361, 362, 364, 365, 366, 367, 368, 370, 371, 372, 373, 374, 375, 376, 377, 379, 380, 381, 382, 383, 384, 386, 387, 388, 389, 391, 393, 395, 396, 397, 398, 399, 400, 401, 409, 410, 423, 427, 428, 429, 430, 433, 434, 435, 436, 445, 446, 455, 456, 465, 466, 475, 476, 485, 486, 494, 495, 513, 514, 520, 526, 535, 536, 538, 539, 540, 541, 542, 543, 547, 548, 549, 550, 551, 552, 564, 565, 566, 567, 573, 575, 581, 583, 584, 593, 600, 601, 602, 604, 605, 606, 607, 608, 609, 610, 612, 631, 637, 643, 644, 668, 669, 671, 683, 684, 687, 688, 695, 697, 714, 722, 735, 739, 740, 741, 746, 750, 751, 752, 753, 761, 766, 800, 801, 802, 810, 811, 812, 820, 821, 829, 830, 837, 838, 845, 846, 858, 859, 869, 870, 880, 881, 891, 897, 902, 903, 904, 905, 913, 925, 926, 927, 932, 933, 934], "summary": {"covered_lines": 312, "num_statements": 329, "percent_covered": 94.83282674772036, "percent_covered_display": "95", "missing_lines": 17, "excluded_lines": 0, "percent_statements_covered": 94.83282674772036, "percent_statements_covered_display": "95"}, "missing_lines": [132, 147, 149, 293, 619, 620, 621, 625, 704, 705, 706, 707, 716, 724, 725, 906, 907], "excluded_lines": [], "functions": {"_extract_pagingcookie": {"executed_lines": [60, 61, 62, 63, 64, 70, 71, 72, 73, 74, 75], "summary": {"covered_lines": 11, "num_statements": 11, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 50}, "_RequestContext.build": {"executed_lines": [98, 99, 100, 101, 102, 103], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 89}, "_ODataBase.__init__": {"executed_lines": [130, 131, 133, 134, 140, 142, 143, 144, 145, 146], "summary": {"covered_lines": 10, "num_statements": 13, "percent_covered": 76.92307692307692, "percent_covered_display": "77", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 76.92307692307692, "percent_statements_covered_display": "77"}, "missing_lines": [132, 147, 149], "excluded_lines": [], "start_line": 120}, "_ODataBase._escape_odata_quotes": {"executed_lines": [158], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 156}, "_ODataBase._normalize_cache_key": {"executed_lines": [163], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 161}, "_ODataBase._lowercase_keys": {"executed_lines": [179, 180, 181], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 166}, "_ODataBase._lowercase_list": {"executed_lines": [189, 190, 191], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 184}, "_ODataBase._extract_logical_table": {"executed_lines": [202, 203, 205, 206, 207, 208, 209, 210], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 194}, "_ODataBase._call_scope": {"executed_lines": [219, 220, 221, 222, 224], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 217}, "_ODataBase._format_key": {"executed_lines": [227, 228, 229, 231, 233, 237, 238, 239, 240, 241], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 226}, "_ODataBase._format_key.esc": {"executed_lines": [235], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 233}, "_ODataBase._build_alternate_key_str": {"executed_lines": [258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 270, 271], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 243}, "_ODataBase._label": {"executed_lines": [274, 275], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 273}, "_ODataBase._to_pascal": {"executed_lines": [287, 288], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 286}, "_ODataBase._normalize_picklist_label": {"executed_lines": [292, 295, 296, 298, 299], "summary": {"covered_lines": 5, "num_statements": 6, "percent_covered": 83.33333333333333, "percent_covered_display": "83", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 83.33333333333333, "percent_statements_covered_display": "83"}, "missing_lines": [293], "excluded_lines": [], "start_line": 290}, "_ODataBase._build_localizedlabels_payload": {"executed_lines": [310, 311, 312, 313, 314, 315, 316, 323, 324, 325], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 305}, "_ODataBase._enum_optionset_payload": {"executed_lines": [343, 344, 345, 348, 349, 350, 352, 353, 356, 358, 360, 361, 362, 364, 365, 366, 367, 368, 370, 371, 372, 373, 374, 375, 376, 377, 379, 380, 381, 382, 383, 384, 386, 387, 388, 389, 391, 393, 395, 396, 397, 398, 399, 400, 401, 409, 410], "summary": {"covered_lines": 47, "num_statements": 47, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 330}, "_ODataBase._attribute_payload": {"executed_lines": [427, 428, 429, 430, 433, 434, 435, 436, 445, 446, 455, 456, 465, 466, 475, 476, 485, 486, 494, 495, 513, 514, 520], "summary": {"covered_lines": 23, "num_statements": 23, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 423}, "_ODataBase._build_create_entity": {"executed_lines": [535, 536, 538, 539, 540, 541, 542, 543, 547, 548, 549, 550, 551, 552, 564, 565, 566, 567], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 526}, "_ODataBase._build_delete_entity": {"executed_lines": [575], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 573}, "_ODataBase._build_get_entity": {"executed_lines": [583, 584], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 581}, "_ODataBase._build_list_entities": {"executed_lines": [600, 601, 602, 604, 605, 606, 607, 608, 609, 610], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 593}, "_ODataBase._build_create_column": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 4, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [619, 620, 621, 625], "excluded_lines": [], "start_line": 612}, "_ODataBase._build_delete_column": {"executed_lines": [637], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 631}, "_ODataBase._build_lookup_field_models": {"executed_lines": [668, 669, 671, 683, 684, 687, 688, 695], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 644}, "_ODataBase._build_create_relationship": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 4, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [704, 705, 706, 707], "excluded_lines": [], "start_line": 697}, "_ODataBase._build_delete_relationship": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [716], "excluded_lines": [], "start_line": 714}, "_ODataBase._build_get_relationship": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [724, 725], "excluded_lines": [], "start_line": 722}, "_ODataBase._sql_guardrails": {"executed_lines": [800, 801, 802, 810, 811, 812, 820, 821, 829, 830, 837, 838, 845, 846, 858, 859, 869, 870, 880, 881, 891], "summary": {"covered_lines": 21, "num_statements": 21, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 766}, "_ODataBase.close": {"executed_lines": [902, 903, 904, 905], "summary": {"covered_lines": 4, "num_statements": 6, "percent_covered": 66.66666666666667, "percent_covered_display": "67", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 66.66666666666667, "percent_statements_covered_display": "67"}, "missing_lines": [906, 907], "excluded_lines": [], "start_line": 897}, "_ODataBase._flush_cache": {"executed_lines": [925, 926, 927, 932, 933, 934], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 913}, "": {"executed_lines": [10, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24, 26, 27, 33, 38, 39, 40, 42, 44, 45, 46, 47, 50, 78, 79, 82, 83, 84, 85, 86, 88, 89, 112, 120, 155, 156, 160, 161, 165, 166, 183, 184, 193, 194, 216, 217, 226, 243, 273, 286, 290, 305, 330, 423, 526, 573, 581, 593, 612, 631, 643, 644, 697, 714, 722, 735, 739, 740, 741, 746, 750, 751, 752, 753, 761, 766, 897, 913], "summary": {"covered_lines": 80, "num_statements": 80, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_RequestContext": {"executed_lines": [98, 99, 100, 101, 102, 103], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 79}, "_ODataBase": {"executed_lines": [130, 131, 133, 134, 140, 142, 143, 144, 145, 146, 158, 163, 179, 180, 181, 189, 190, 191, 202, 203, 205, 206, 207, 208, 209, 210, 219, 220, 221, 222, 224, 227, 228, 229, 231, 233, 235, 237, 238, 239, 240, 241, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 270, 271, 274, 275, 287, 288, 292, 295, 296, 298, 299, 310, 311, 312, 313, 314, 315, 316, 323, 324, 325, 343, 344, 345, 348, 349, 350, 352, 353, 356, 358, 360, 361, 362, 364, 365, 366, 367, 368, 370, 371, 372, 373, 374, 375, 376, 377, 379, 380, 381, 382, 383, 384, 386, 387, 388, 389, 391, 393, 395, 396, 397, 398, 399, 400, 401, 409, 410, 427, 428, 429, 430, 433, 434, 435, 436, 445, 446, 455, 456, 465, 466, 475, 476, 485, 486, 494, 495, 513, 514, 520, 535, 536, 538, 539, 540, 541, 542, 543, 547, 548, 549, 550, 551, 552, 564, 565, 566, 567, 575, 583, 584, 600, 601, 602, 604, 605, 606, 607, 608, 609, 610, 637, 668, 669, 671, 683, 684, 687, 688, 695, 800, 801, 802, 810, 811, 812, 820, 821, 829, 830, 837, 838, 845, 846, 858, 859, 869, 870, 880, 881, 891, 902, 903, 904, 905, 925, 926, 927, 932, 933, 934], "summary": {"covered_lines": 215, "num_statements": 232, "percent_covered": 92.67241379310344, "percent_covered_display": "93", "missing_lines": 17, "excluded_lines": 0, "percent_statements_covered": 92.67241379310344, "percent_statements_covered_display": "93"}, "missing_lines": [132, 147, 149, 293, 619, 620, 621, 625, 704, 705, 706, 707, 716, 724, 725, 906, 907], "excluded_lines": [], "start_line": 112}, "": {"executed_lines": [10, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24, 26, 27, 33, 38, 39, 40, 42, 44, 45, 46, 47, 50, 60, 61, 62, 63, 64, 70, 71, 72, 73, 74, 75, 78, 79, 82, 83, 84, 85, 86, 88, 89, 112, 120, 155, 156, 160, 161, 165, 166, 183, 184, 193, 194, 216, 217, 226, 243, 273, 286, 290, 305, 330, 423, 526, 573, 581, 593, 612, 631, 643, 644, 697, 714, 722, 735, 739, 740, 741, 746, 750, 751, 752, 753, 761, 766, 897, 913], "summary": {"covered_lines": 91, "num_statements": 91, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\data\\_raw_request.py": {"executed_lines": [6, 8, 9, 11, 14, 15, 29, 30, 31, 32, 33], "summary": {"covered_lines": 11, "num_statements": 11, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [6, 8, 9, 11, 14, 15, 29, 30, 31, 32, 33], "summary": {"covered_lines": 11, "num_statements": 11, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_RawRequest": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 15}, "": {"executed_lines": [6, 8, 9, 11, 14, 15, 29, 30, 31, 32, 33], "summary": {"covered_lines": 11, "num_statements": 11, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\data\\_relationships.py": {"executed_lines": [10, 12, 14, 15, 18, 28, 51, 54, 55, 57, 58, 59, 61, 64, 66, 74, 94, 96, 98, 99, 100, 102, 105, 107, 114, 123, 124, 125, 126, 128, 140, 141, 142, 143, 144, 145, 147, 171, 172, 173, 174, 175, 176, 177, 178, 180, 210, 211, 213, 214, 215, 220, 228, 229, 230, 231, 232, 233, 234, 236, 237, 238, 240, 241, 242, 244, 246, 256, 257, 258, 259], "summary": {"covered_lines": 71, "num_statements": 71, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"_RelationshipOperationsMixin._create_one_to_many_relationship": {"executed_lines": [51, 54, 55, 57, 58, 59, 61, 64, 66], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 28}, "_RelationshipOperationsMixin._create_many_to_many_relationship": {"executed_lines": [94, 96, 98, 99, 100, 102, 105, 107], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 74}, "_RelationshipOperationsMixin._delete_relationship": {"executed_lines": [123, 124, 125, 126], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 114}, "_RelationshipOperationsMixin._get_relationship": {"executed_lines": [140, 141, 142, 143, 144, 145], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 128}, "_RelationshipOperationsMixin._list_relationships": {"executed_lines": [171, 172, 173, 174, 175, 176, 177, 178], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 147}, "_RelationshipOperationsMixin._list_table_relationships": {"executed_lines": [210, 211, 213, 214, 215, 220, 228, 229, 230, 231, 232, 233, 234, 236, 237, 238, 240, 241, 242, 244], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 180}, "_RelationshipOperationsMixin._extract_id_from_header": {"executed_lines": [256, 257, 258, 259], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 246}, "": {"executed_lines": [10, 12, 14, 15, 18, 28, 74, 114, 128, 147, 180, 246], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_RelationshipOperationsMixin": {"executed_lines": [51, 54, 55, 57, 58, 59, 61, 64, 66, 94, 96, 98, 99, 100, 102, 105, 107, 123, 124, 125, 126, 140, 141, 142, 143, 144, 145, 171, 172, 173, 174, 175, 176, 177, 178, 210, 211, 213, 214, 215, 220, 228, 229, 230, 231, 232, 233, 234, 236, 237, 238, 240, 241, 242, 244, 256, 257, 258, 259], "summary": {"covered_lines": 59, "num_statements": 59, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 18}, "": {"executed_lines": [10, 12, 14, 15, 18, 28, 74, 114, 128, 147, 180, 246], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\data\\_upload.py": {"executed_lines": [6, 8, 11, 14, 43, 46, 49, 50, 51, 52, 53, 54, 56, 59, 61, 63, 64, 65, 66, 67, 70, 72, 73, 76, 77, 78, 80, 90, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 109, 110, 112, 114, 115, 117, 147, 148, 150, 151, 152, 153, 154, 155, 156, 157, 158, 161, 162, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 193, 194, 195], "summary": {"covered_lines": 87, "num_statements": 87, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"_FileUploadMixin._upload_file": {"executed_lines": [43, 46, 49, 50, 51, 52, 53, 54, 56, 59, 61, 63, 64, 65, 66, 67, 70, 72, 73, 76, 77, 78], "summary": {"covered_lines": 22, "num_statements": 22, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 14}, "_FileUploadMixin._upload_file_small": {"executed_lines": [90, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 109, 110, 112, 114, 115], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 80}, "_FileUploadMixin._upload_file_chunk": {"executed_lines": [147, 148, 150, 151, 152, 153, 154, 155, 156, 157, 158, 161, 162, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 193, 194, 195], "summary": {"covered_lines": 39, "num_statements": 39, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 117}, "": {"executed_lines": [6, 8, 11, 14, 80, 117], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_FileUploadMixin": {"executed_lines": [43, 46, 49, 50, 51, 52, 53, 54, 56, 59, 61, 63, 64, 65, 66, 67, 70, 72, 73, 76, 77, 78, 90, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 109, 110, 112, 114, 115, 147, 148, 150, 151, 152, 153, 154, 155, 156, 157, 158, 161, 162, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 193, 194, 195], "summary": {"covered_lines": 81, "num_statements": 81, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 11}, "": {"executed_lines": [6, 8, 11, 14, 80, 117], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\extensions\\__init__.py": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [9], "excluded_lines": [], "functions": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [9], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [9], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\models\\__init__.py": {"executed_lines": [19], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [19], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [19], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\models\\batch.py": {"executed_lines": [6, 8, 9, 11, 14, 15, 39, 40, 41, 42, 43, 44, 46, 47, 49, 52, 53, 71, 73, 74, 76, 78, 79, 81, 83, 84, 86, 88, 89, 106], "summary": {"covered_lines": 30, "num_statements": 30, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"BatchItemResponse.is_success": {"executed_lines": [49], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 47}, "BatchResult.succeeded": {"executed_lines": [76], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 74}, "BatchResult.failed": {"executed_lines": [81], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 79}, "BatchResult.has_errors": {"executed_lines": [86], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 84}, "BatchResult.entity_ids": {"executed_lines": [106], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 89}, "": {"executed_lines": [6, 8, 9, 11, 14, 15, 39, 40, 41, 42, 43, 44, 46, 47, 52, 53, 71, 73, 74, 78, 79, 83, 84, 88, 89], "summary": {"covered_lines": 25, "num_statements": 25, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"BatchItemResponse": {"executed_lines": [49], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 15}, "BatchResult": {"executed_lines": [76, 81, 86, 106], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 53}, "": {"executed_lines": [6, 8, 9, 11, 14, 15, 39, 40, 41, 42, 43, 44, 46, 47, 52, 53, 71, 73, 74, 78, 79, 83, 84, 88, 89], "summary": {"covered_lines": 25, "num_statements": 25, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\models\\filters.py": {"executed_lines": [34, 36, 37, 38, 39, 41, 67, 78, 79, 81, 82, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 95, 96, 97, 98, 99, 100, 101, 102, 103, 105, 113, 123, 125, 127, 128, 129, 130, 132, 133, 134, 135, 137, 138, 140, 141, 143, 144, 152, 155, 157, 158, 159, 160, 162, 163, 166, 169, 171, 172, 173, 174, 176, 177, 180, 183, 185, 186, 187, 189, 190, 193, 196, 198, 199, 200, 202, 203, 206, 209, 211, 212, 214, 215, 218, 221, 223, 224, 225, 226, 227, 229, 231, 232, 233, 236, 239, 241, 242, 243, 244, 245, 247, 249, 250, 251, 254, 257, 259, 260, 262, 263, 271, 282, 285, 292, 295, 302, 305, 312, 315, 322, 325, 332, 335, 342, 345, 352, 355, 362, 365, 380, 383, 389, 392, 398, 401, 416, 419, 434, 437, 452, 455, 465], "summary": {"covered_lines": 144, "num_statements": 144, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"_format_value": {"executed_lines": [78, 79, 81, 82, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 95, 96, 97, 98, 99, 100, 101, 102, 103, 105], "summary": {"covered_lines": 24, "num_statements": 24, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 67}, "FilterExpression.to_odata": {"executed_lines": [125], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 123}, "FilterExpression.__and__": {"executed_lines": [128, 129, 130], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 127}, "FilterExpression.__or__": {"executed_lines": [133, 134, 135], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 132}, "FilterExpression.__invert__": {"executed_lines": [138], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 137}, "FilterExpression.__str__": {"executed_lines": [141], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 140}, "FilterExpression.__repr__": {"executed_lines": [144], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 143}, "_ComparisonFilter.__init__": {"executed_lines": [158, 159, 160], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 157}, "_ComparisonFilter.to_odata": {"executed_lines": [163], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 162}, "_FunctionFilter.__init__": {"executed_lines": [172, 173, 174], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 171}, "_FunctionFilter.to_odata": {"executed_lines": [177], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 176}, "_AndFilter.__init__": {"executed_lines": [186, 187], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 185}, "_AndFilter.to_odata": {"executed_lines": [190], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 189}, "_OrFilter.__init__": {"executed_lines": [199, 200], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 198}, "_OrFilter.to_odata": {"executed_lines": [203], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 202}, "_NotFilter.__init__": {"executed_lines": [212], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 211}, "_NotFilter.to_odata": {"executed_lines": [215], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 214}, "_InFilter.__init__": {"executed_lines": [224, 225, 226, 227], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 223}, "_InFilter.to_odata": {"executed_lines": [231, 232, 233], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 229}, "_NotInFilter.__init__": {"executed_lines": [242, 243, 244, 245], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 241}, "_NotInFilter.to_odata": {"executed_lines": [249, 250, 251], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 247}, "_RawFilter.__init__": {"executed_lines": [260], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 259}, "_RawFilter.to_odata": {"executed_lines": [263], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 262}, "eq": {"executed_lines": [282], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 271}, "ne": {"executed_lines": [292], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 285}, "gt": {"executed_lines": [302], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 295}, "ge": {"executed_lines": [312], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 305}, "lt": {"executed_lines": [322], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 315}, "le": {"executed_lines": [332], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 325}, "contains": {"executed_lines": [342], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 335}, "startswith": {"executed_lines": [352], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 345}, "endswith": {"executed_lines": [362], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 355}, "between": {"executed_lines": [380], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 365}, "is_null": {"executed_lines": [389], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 383}, "is_not_null": {"executed_lines": [398], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 392}, "filter_in": {"executed_lines": [416], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 401}, "not_in": {"executed_lines": [434], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 419}, "not_between": {"executed_lines": [452], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 437}, "raw": {"executed_lines": [465], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 455}, "": {"executed_lines": [34, 36, 37, 38, 39, 41, 67, 113, 123, 127, 132, 137, 140, 143, 152, 155, 157, 162, 166, 169, 171, 176, 180, 183, 185, 189, 193, 196, 198, 202, 206, 209, 211, 214, 218, 221, 223, 229, 236, 239, 241, 247, 254, 257, 259, 262, 271, 285, 295, 305, 315, 325, 335, 345, 355, 365, 383, 392, 401, 419, 437, 455], "summary": {"covered_lines": 62, "num_statements": 62, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"FilterExpression": {"executed_lines": [125, 128, 129, 130, 133, 134, 135, 138, 141, 144], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 113}, "_ComparisonFilter": {"executed_lines": [158, 159, 160, 163], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 152}, "_FunctionFilter": {"executed_lines": [172, 173, 174, 177], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 166}, "_AndFilter": {"executed_lines": [186, 187, 190], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 180}, "_OrFilter": {"executed_lines": [199, 200, 203], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 193}, "_NotFilter": {"executed_lines": [212, 215], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 206}, "_InFilter": {"executed_lines": [224, 225, 226, 227, 231, 232, 233], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 218}, "_NotInFilter": {"executed_lines": [242, 243, 244, 245, 249, 250, 251], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 236}, "_RawFilter": {"executed_lines": [260, 263], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 254}, "": {"executed_lines": [34, 36, 37, 38, 39, 41, 67, 78, 79, 81, 82, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 95, 96, 97, 98, 99, 100, 101, 102, 103, 105, 113, 123, 127, 132, 137, 140, 143, 152, 155, 157, 162, 166, 169, 171, 176, 180, 183, 185, 189, 193, 196, 198, 202, 206, 209, 211, 214, 218, 221, 223, 229, 236, 239, 241, 247, 254, 257, 259, 262, 271, 282, 285, 292, 295, 302, 305, 312, 315, 322, 325, 332, 335, 342, 345, 352, 355, 362, 365, 380, 383, 389, 392, 398, 401, 416, 419, 434, 437, 452, 455, 465], "summary": {"covered_lines": 102, "num_statements": 102, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\models\\labels.py": {"executed_lines": [6, 8, 9, 11, 17, 18, 31, 32, 33, 35, 49, 54, 55, 56, 59, 60, 73, 74, 75, 77, 93, 98, 99, 100, 101, 102, 103, 104, 107], "summary": {"covered_lines": 29, "num_statements": 29, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"LocalizedLabel.to_dict": {"executed_lines": [49, 54, 55, 56], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 35}, "Label.to_dict": {"executed_lines": [93, 98, 99, 100, 101, 102, 103, 104], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 77}, "": {"executed_lines": [6, 8, 9, 11, 17, 18, 31, 32, 33, 35, 59, 60, 73, 74, 75, 77, 107], "summary": {"covered_lines": 17, "num_statements": 17, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"LocalizedLabel": {"executed_lines": [49, 54, 55, 56], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 18}, "Label": {"executed_lines": [93, 98, 99, 100, 101, 102, 103, 104], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 60}, "": {"executed_lines": [6, 8, 9, 11, 17, 18, 31, 32, 33, 35, 59, 60, 73, 74, 75, 77, 107], "summary": {"covered_lines": 17, "num_statements": 17, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\models\\query_builder.py": {"executed_lines": [47, 49, 51, 53, 54, 56, 59, 66, 67, 68, 69, 70, 71, 72, 73, 74, 77, 102, 103, 104, 105, 106, 107, 109, 115, 116, 118, 124, 125, 127, 134, 135, 136, 138, 144, 145, 147, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 167, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 207, 220, 221, 225, 232, 233, 235, 242, 243, 245, 252, 253, 255, 262, 263, 265, 272, 273, 275, 282, 283, 287, 294, 295, 297, 304, 305, 307, 314, 315, 319, 325, 326, 328, 334, 335, 339, 353, 354, 356, 370, 371, 373, 386, 387, 389, 402, 403, 405, 425, 426, 430, 453, 454, 455, 456, 460, 469, 470, 471, 475, 482, 483, 484, 485, 487, 497, 498, 499, 500, 502, 518, 519, 521, 548, 549, 551, 574, 575, 579, 602, 603, 604, 606, 607, 611, 623, 624, 625, 626, 627, 628, 629, 630, 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645, 646, 650, 659, 660, 667, 713, 714, 718, 719, 720, 722, 734, 735, 737, 738, 739, 741, 745, 775, 776, 780, 781, 782], "summary": {"covered_lines": 195, "num_statements": 195, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"ExpandOption.__init__": {"executed_lines": [103, 104, 105, 106, 107], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 102}, "ExpandOption.select": {"executed_lines": [115, 116], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 109}, "ExpandOption.filter": {"executed_lines": [124, 125], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 118}, "ExpandOption.order_by": {"executed_lines": [134, 135, 136], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 127}, "ExpandOption.top": {"executed_lines": [144, 145], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 138}, "ExpandOption.to_odata": {"executed_lines": [153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 147}, "QueryBuilder.__init__": {"executed_lines": [191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 190}, "QueryBuilder.select": {"executed_lines": [220, 221], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 207}, "QueryBuilder.filter_eq": {"executed_lines": [232, 233], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 225}, "QueryBuilder.filter_ne": {"executed_lines": [242, 243], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 235}, "QueryBuilder.filter_gt": {"executed_lines": [252, 253], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 245}, "QueryBuilder.filter_ge": {"executed_lines": [262, 263], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 255}, "QueryBuilder.filter_lt": {"executed_lines": [272, 273], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 265}, "QueryBuilder.filter_le": {"executed_lines": [282, 283], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 275}, "QueryBuilder.filter_contains": {"executed_lines": [294, 295], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 287}, "QueryBuilder.filter_startswith": {"executed_lines": [304, 305], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 297}, "QueryBuilder.filter_endswith": {"executed_lines": [314, 315], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 307}, "QueryBuilder.filter_null": {"executed_lines": [325, 326], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 319}, "QueryBuilder.filter_not_null": {"executed_lines": [334, 335], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 328}, "QueryBuilder.filter_in": {"executed_lines": [353, 354], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 339}, "QueryBuilder.filter_not_in": {"executed_lines": [370, 371], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 356}, "QueryBuilder.filter_between": {"executed_lines": [386, 387], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 373}, "QueryBuilder.filter_not_between": {"executed_lines": [402, 403], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 389}, "QueryBuilder.filter_raw": {"executed_lines": [425, 426], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 405}, "QueryBuilder.where": {"executed_lines": [453, 454, 455, 456], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 430}, "QueryBuilder.order_by": {"executed_lines": [469, 470, 471], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 460}, "QueryBuilder.top": {"executed_lines": [482, 483, 484, 485], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 475}, "QueryBuilder.page_size": {"executed_lines": [497, 498, 499, 500], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 487}, "QueryBuilder.count": {"executed_lines": [518, 519], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 502}, "QueryBuilder.include_formatted_values": {"executed_lines": [548, 549], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 521}, "QueryBuilder.include_annotations": {"executed_lines": [574, 575], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 551}, "QueryBuilder.expand": {"executed_lines": [602, 603, 604, 606, 607], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 579}, "QueryBuilder.build": {"executed_lines": [623, 624, 625, 626, 627, 628, 629, 630, 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645, 646], "summary": {"covered_lines": 23, "num_statements": 23, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 611}, "QueryBuilder._validate_constraints": {"executed_lines": [659, 660], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 650}, "QueryBuilder.execute": {"executed_lines": [713, 714, 718, 719, 720, 722, 734, 735, 737, 741], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 667}, "QueryBuilder.execute._flat": {"executed_lines": [738, 739], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 737}, "QueryBuilder.to_dataframe": {"executed_lines": [775, 776, 780, 781, 782], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 745}, "": {"executed_lines": [47, 49, 51, 53, 54, 56, 59, 66, 67, 68, 69, 70, 71, 72, 73, 74, 77, 102, 109, 118, 127, 138, 147, 167, 190, 207, 225, 235, 245, 255, 265, 275, 287, 297, 307, 319, 328, 339, 356, 373, 389, 405, 430, 460, 475, 487, 502, 521, 551, 579, 611, 650, 667, 745], "summary": {"covered_lines": 54, "num_statements": 54, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"QueryParams": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 59}, "ExpandOption": {"executed_lines": [103, 104, 105, 106, 107, 115, 116, 124, 125, 134, 135, 136, 144, 145, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164], "summary": {"covered_lines": 26, "num_statements": 26, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 77}, "QueryBuilder": {"executed_lines": [191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 220, 221, 232, 233, 242, 243, 252, 253, 262, 263, 272, 273, 282, 283, 294, 295, 304, 305, 314, 315, 325, 326, 334, 335, 353, 354, 370, 371, 386, 387, 402, 403, 425, 426, 453, 454, 455, 456, 469, 470, 471, 482, 483, 484, 485, 497, 498, 499, 500, 518, 519, 548, 549, 574, 575, 602, 603, 604, 606, 607, 623, 624, 625, 626, 627, 628, 629, 630, 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645, 646, 659, 660, 713, 714, 718, 719, 720, 722, 734, 735, 737, 738, 739, 741, 775, 776, 780, 781, 782], "summary": {"covered_lines": 115, "num_statements": 115, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 167}, "": {"executed_lines": [47, 49, 51, 53, 54, 56, 59, 66, 67, 68, 69, 70, 71, 72, 73, 74, 77, 102, 109, 118, 127, 138, 147, 167, 190, 207, 225, 235, 245, 255, 265, 275, 287, 297, 307, 319, 328, 339, 356, 373, 389, 405, 430, 460, 475, 487, 502, 521, 551, 579, 611, 650, 667, 745], "summary": {"covered_lines": 54, "num_statements": 54, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\models\\record.py": {"executed_lines": [6, 8, 9, 11, 13, 16, 17, 41, 42, 43, 44, 48, 49, 51, 52, 54, 55, 57, 58, 60, 61, 63, 64, 66, 68, 70, 72, 74, 76, 78, 80, 84, 85, 106, 107, 108, 112, 114], "summary": {"covered_lines": 38, "num_statements": 38, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"Record.__getitem__": {"executed_lines": [49], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 48}, "Record.__setitem__": {"executed_lines": [52], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 51}, "Record.__delitem__": {"executed_lines": [55], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 54}, "Record.__contains__": {"executed_lines": [58], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 57}, "Record.__iter__": {"executed_lines": [61], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 60}, "Record.__len__": {"executed_lines": [64], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 63}, "Record.get": {"executed_lines": [68], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 66}, "Record.keys": {"executed_lines": [72], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 70}, "Record.values": {"executed_lines": [76], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 74}, "Record.items": {"executed_lines": [80], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 78}, "Record.from_api_response": {"executed_lines": [106, 107, 108], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 85}, "Record.to_dict": {"executed_lines": [114], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 112}, "": {"executed_lines": [6, 8, 9, 11, 13, 16, 17, 41, 42, 43, 44, 48, 51, 54, 57, 60, 63, 66, 70, 74, 78, 84, 85, 112], "summary": {"covered_lines": 24, "num_statements": 24, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"Record": {"executed_lines": [49, 52, 55, 58, 61, 64, 68, 72, 76, 80, 106, 107, 108, 114], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 17}, "": {"executed_lines": [6, 8, 9, 11, 13, 16, 17, 41, 42, 43, 44, 48, 51, 54, 57, 60, 63, 66, 70, 74, 78, 84, 85, 112], "summary": {"covered_lines": 24, "num_statements": 24, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\models\\relationship.py": {"executed_lines": [6, 8, 9, 11, 20, 23, 24, 52, 53, 54, 55, 56, 57, 58, 60, 77, 85, 86, 87, 90, 91, 116, 117, 118, 119, 120, 122, 142, 154, 155, 156, 157, 158, 161, 162, 185, 186, 187, 188, 189, 190, 191, 193, 215, 223, 224, 225, 226, 227, 230, 231, 251, 252, 253, 254, 255, 257, 278, 279, 286, 287, 288, 291, 292, 324, 325, 326, 329, 330, 331, 334, 335, 337, 338, 361, 370, 371, 391, 399, 400, 414, 415, 416, 418, 419, 429, 430, 437, 440], "summary": {"covered_lines": 89, "num_statements": 89, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"CascadeConfiguration.to_dict": {"executed_lines": [77, 85, 86, 87], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 60}, "LookupAttributeMetadata.to_dict": {"executed_lines": [142, 154, 155, 156, 157, 158], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 122}, "OneToManyRelationshipMetadata.to_dict": {"executed_lines": [215, 223, 224, 225, 226, 227], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 193}, "ManyToManyRelationshipMetadata.to_dict": {"executed_lines": [278, 279, 286, 287, 288], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 257}, "RelationshipInfo.from_one_to_many": {"executed_lines": [361], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 338}, "RelationshipInfo.from_many_to_many": {"executed_lines": [391], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 371}, "RelationshipInfo.from_api_response": {"executed_lines": [414, 415, 416, 418, 419, 429, 430, 437], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 400}, "": {"executed_lines": [6, 8, 9, 11, 20, 23, 24, 52, 53, 54, 55, 56, 57, 58, 60, 90, 91, 116, 117, 118, 119, 120, 122, 161, 162, 185, 186, 187, 188, 189, 190, 191, 193, 230, 231, 251, 252, 253, 254, 255, 257, 291, 292, 324, 325, 326, 329, 330, 331, 334, 335, 337, 338, 370, 371, 399, 400, 440], "summary": {"covered_lines": 58, "num_statements": 58, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"CascadeConfiguration": {"executed_lines": [77, 85, 86, 87], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 24}, "LookupAttributeMetadata": {"executed_lines": [142, 154, 155, 156, 157, 158], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 91}, "OneToManyRelationshipMetadata": {"executed_lines": [215, 223, 224, 225, 226, 227], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 162}, "ManyToManyRelationshipMetadata": {"executed_lines": [278, 279, 286, 287, 288], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 231}, "RelationshipInfo": {"executed_lines": [361, 391, 414, 415, 416, 418, 419, 429, 430, 437], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 292}, "": {"executed_lines": [6, 8, 9, 11, 20, 23, 24, 52, 53, 54, 55, 56, 57, 58, 60, 90, 91, 116, 117, 118, 119, 120, 122, 161, 162, 185, 186, 187, 188, 189, 190, 191, 193, 230, 231, 251, 252, 253, 254, 255, 257, 291, 292, 324, 325, 326, 329, 330, 331, 334, 335, 337, 338, 370, 371, 399, 400, 440], "summary": {"covered_lines": 58, "num_statements": 58, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\models\\table_info.py": {"executed_lines": [6, 8, 9, 11, 14, 15, 36, 37, 38, 39, 40, 41, 42, 43, 45, 46, 54, 55, 56, 59, 60, 61, 64, 65, 67, 79, 80, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 123, 135, 137, 139, 140, 141, 142, 143, 145, 146, 147, 148, 149, 151, 152, 154, 155, 157, 159, 160, 161, 162, 164, 166, 168, 170, 172, 174, 178, 179, 189, 199, 200, 208, 209, 210, 213, 214, 215, 217, 230, 232, 235, 236, 249, 250, 251, 252, 254, 255, 262], "summary": {"covered_lines": 88, "num_statements": 88, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"ColumnInfo.from_api_response": {"executed_lines": [54, 55, 56, 59, 60, 61, 64, 65, 67], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 46}, "TableInfo._resolve_key": {"executed_lines": [137], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 135}, "TableInfo.__getitem__": {"executed_lines": [140, 141, 142, 143], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 139}, "TableInfo.__contains__": {"executed_lines": [146, 147, 148, 149], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 145}, "TableInfo.__iter__": {"executed_lines": [152], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 151}, "TableInfo.__len__": {"executed_lines": [155], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 154}, "TableInfo.get": {"executed_lines": [159, 160, 161, 162], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 157}, "TableInfo.keys": {"executed_lines": [166], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 164}, "TableInfo.values": {"executed_lines": [170], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 168}, "TableInfo.items": {"executed_lines": [174], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 172}, "TableInfo.from_dict": {"executed_lines": [189], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 179}, "TableInfo.from_api_response": {"executed_lines": [208, 209, 210, 213, 214, 215, 217], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 200}, "TableInfo.to_dict": {"executed_lines": [232], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 230}, "AlternateKeyInfo.from_api_response": {"executed_lines": [262], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 255}, "": {"executed_lines": [6, 8, 9, 11, 14, 15, 36, 37, 38, 39, 40, 41, 42, 43, 45, 46, 79, 80, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 123, 135, 139, 145, 151, 154, 157, 164, 168, 172, 178, 179, 199, 200, 230, 235, 236, 249, 250, 251, 252, 254, 255], "summary": {"covered_lines": 51, "num_statements": 51, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"ColumnInfo": {"executed_lines": [54, 55, 56, 59, 60, 61, 64, 65, 67], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 15}, "TableInfo": {"executed_lines": [137, 140, 141, 142, 143, 146, 147, 148, 149, 152, 155, 159, 160, 161, 162, 166, 170, 174, 189, 208, 209, 210, 213, 214, 215, 217, 232], "summary": {"covered_lines": 27, "num_statements": 27, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 80}, "AlternateKeyInfo": {"executed_lines": [262], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 236}, "": {"executed_lines": [6, 8, 9, 11, 14, 15, 36, 37, 38, 39, 40, 41, 42, 43, 45, 46, 79, 80, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 123, 135, 139, 145, 151, 154, 157, 164, 168, 172, 178, 179, 199, 200, 230, 235, 236, 249, 250, 251, 252, 254, 255], "summary": {"covered_lines": 51, "num_statements": 51, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\models\\upsert.py": {"executed_lines": [6, 8, 9, 11, 14, 15, 38, 39], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [6, 8, 9, 11, 14, 15, 38, 39], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"UpsertItem": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 15}, "": {"executed_lines": [6, 8, 9, 11, 14, 15, 38, 39], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\operations\\__init__.py": {"executed_lines": [11, 13], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [11, 13], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [11, 13], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\operations\\batch.py": {"executed_lines": [6, 8, 10, 12, 13, 14, 35, 36, 37, 42, 47, 64, 75, 76, 78, 101, 103, 116, 118, 128, 131, 149, 150, 151, 153, 154, 156, 157, 165, 177, 178, 180, 197, 199, 219, 221, 244, 246, 274, 276, 316, 317, 318, 319, 320, 321, 322, 323, 325, 326, 329, 351, 352, 354, 383, 393, 402, 404, 413, 415, 436, 438, 450, 452, 465, 467, 484, 486, 500, 502, 509, 511, 520, 522, 558, 578, 589, 590, 592, 610, 611, 612, 620, 643, 644, 646, 665, 666, 667, 668, 670, 672, 673, 674, 675, 679, 681, 716, 717, 718, 719, 720, 721, 723, 724, 725, 726, 730, 732, 733, 738, 740, 741, 742, 743, 744, 745, 747, 749, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782, 790, 820, 821, 822, 823, 824, 825, 826, 827, 829, 844, 845, 846, 848, 866, 867, 870, 886, 887, 889, 895], "summary": {"covered_lines": 150, "num_statements": 151, "percent_covered": 99.33774834437087, "percent_covered_display": "99", "missing_lines": 1, "excluded_lines": 2, "percent_statements_covered": 99.33774834437087, "percent_statements_covered_display": "99"}, "missing_lines": [734], "excluded_lines": [44, 45], "functions": {"ChangeSetRecordOperations.__init__": {"executed_lines": [76], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 75}, "ChangeSetRecordOperations.create": {"executed_lines": [101], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 78}, "ChangeSetRecordOperations.update": {"executed_lines": [116], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 103}, "ChangeSetRecordOperations.delete": {"executed_lines": [128], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 118}, "ChangeSet.__init__": {"executed_lines": [150, 151], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 149}, "ChangeSet.__enter__": {"executed_lines": [154], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 153}, "ChangeSet.__exit__": {"executed_lines": [157], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 156}, "BatchRecordOperations.__init__": {"executed_lines": [178], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 177}, "BatchRecordOperations.create": {"executed_lines": [197], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 180}, "BatchRecordOperations.update": {"executed_lines": [219], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 199}, "BatchRecordOperations.delete": {"executed_lines": [244], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 221}, "BatchRecordOperations.get": {"executed_lines": [274], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 246}, "BatchRecordOperations.upsert": {"executed_lines": [316, 317, 318, 319, 320, 321, 322, 323, 325, 326], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 276}, "BatchTableOperations.__init__": {"executed_lines": [352], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 351}, "BatchTableOperations.create": {"executed_lines": [383], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 354}, "BatchTableOperations.delete": {"executed_lines": [402], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 393}, "BatchTableOperations.get": {"executed_lines": [413], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 404}, "BatchTableOperations.list": {"executed_lines": [436], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 415}, "BatchTableOperations.add_columns": {"executed_lines": [450], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 438}, "BatchTableOperations.remove_columns": {"executed_lines": [465], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 452}, "BatchTableOperations.create_one_to_many_relationship": {"executed_lines": [484], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 467}, "BatchTableOperations.create_many_to_many_relationship": {"executed_lines": [500], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 486}, "BatchTableOperations.delete_relationship": {"executed_lines": [509], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 502}, "BatchTableOperations.get_relationship": {"executed_lines": [520], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 511}, "BatchTableOperations.create_lookup_field": {"executed_lines": [558], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 522}, "BatchQueryOperations.__init__": {"executed_lines": [590], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 589}, "BatchQueryOperations.sql": {"executed_lines": [610, 611, 612], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 592}, "BatchDataFrameOperations.__init__": {"executed_lines": [644], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 643}, "BatchDataFrameOperations.create": {"executed_lines": [665, 666, 667, 668, 670, 672, 673, 674, 675, 679], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 646}, "BatchDataFrameOperations.update": {"executed_lines": [716, 717, 718, 719, 720, 721, 723, 724, 725, 726, 730, 732, 733, 738, 740, 741, 742, 743, 744, 745, 747], "summary": {"covered_lines": 21, "num_statements": 22, "percent_covered": 95.45454545454545, "percent_covered_display": "95", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 95.45454545454545, "percent_statements_covered_display": "95"}, "missing_lines": [734], "excluded_lines": [], "start_line": 681}, "BatchDataFrameOperations.delete": {"executed_lines": [773, 774, 775, 776, 777, 778, 779, 780, 781, 782], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 749}, "BatchRequest.__init__": {"executed_lines": [821, 822, 823, 824, 825, 826, 827], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 820}, "BatchRequest.changeset": {"executed_lines": [844, 845, 846], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 829}, "BatchRequest.execute": {"executed_lines": [866, 867], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 848}, "BatchOperations.__init__": {"executed_lines": [887], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 886}, "BatchOperations.new": {"executed_lines": [895], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 889}, "": {"executed_lines": [6, 8, 10, 12, 13, 14, 35, 36, 37, 42, 47, 64, 75, 78, 103, 118, 131, 149, 153, 156, 165, 177, 180, 199, 221, 246, 276, 329, 351, 354, 393, 404, 415, 438, 452, 467, 486, 502, 511, 522, 578, 589, 592, 620, 643, 646, 681, 749, 790, 820, 829, 848, 870, 886, 889], "summary": {"covered_lines": 55, "num_statements": 55, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [44, 45], "start_line": 1}}, "classes": {"ChangeSetRecordOperations": {"executed_lines": [76, 101, 116, 128], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 64}, "ChangeSet": {"executed_lines": [150, 151, 154, 157], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 131}, "BatchRecordOperations": {"executed_lines": [178, 197, 219, 244, 274, 316, 317, 318, 319, 320, 321, 322, 323, 325, 326], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 165}, "BatchTableOperations": {"executed_lines": [352, 383, 402, 413, 436, 450, 465, 484, 500, 509, 520, 558], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 329}, "BatchQueryOperations": {"executed_lines": [590, 610, 611, 612], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 578}, "BatchDataFrameOperations": {"executed_lines": [644, 665, 666, 667, 668, 670, 672, 673, 674, 675, 679, 716, 717, 718, 719, 720, 721, 723, 724, 725, 726, 730, 732, 733, 738, 740, 741, 742, 743, 744, 745, 747, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782], "summary": {"covered_lines": 42, "num_statements": 43, "percent_covered": 97.67441860465117, "percent_covered_display": "98", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 97.67441860465117, "percent_statements_covered_display": "98"}, "missing_lines": [734], "excluded_lines": [], "start_line": 620}, "BatchRequest": {"executed_lines": [821, 822, 823, 824, 825, 826, 827, 844, 845, 846, 866, 867], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 790}, "BatchOperations": {"executed_lines": [887, 895], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 870}, "": {"executed_lines": [6, 8, 10, 12, 13, 14, 35, 36, 37, 42, 47, 64, 75, 78, 103, 118, 131, 149, 153, 156, 165, 177, 180, 199, 221, 246, 276, 329, 351, 354, 393, 404, 415, 438, 452, 467, 486, 502, 511, 522, 578, 589, 592, 620, 643, 646, 681, 749, 790, 820, 829, 848, 870, 886, 889], "summary": {"covered_lines": 55, "num_statements": 55, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [44, 45], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\operations\\dataframe.py": {"executed_lines": [6, 8, 10, 12, 18, 21, 51, 52, 56, 91, 92, 93, 94, 98, 167, 168, 169, 170, 171, 172, 176, 181, 183, 184, 195, 197, 198, 199, 203, 238, 239, 241, 242, 244, 247, 248, 249, 254, 256, 257, 259, 263, 324, 325, 326, 327, 328, 329, 331, 332, 333, 334, 338, 340, 341, 342, 345, 348, 349, 350, 351, 352, 354, 355, 357, 361, 394, 395, 397, 398, 399, 401, 402, 403, 406, 408, 409, 410, 411], "summary": {"covered_lines": 79, "num_statements": 79, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [14, 15], "functions": {"DataFrameOperations.__init__": {"executed_lines": [52], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 51}, "DataFrameOperations.sql": {"executed_lines": [91, 92, 93, 94], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 56}, "DataFrameOperations.get": {"executed_lines": [167, 168, 169, 170, 171, 172, 176, 181, 183, 184, 195, 197, 198, 199], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 98}, "DataFrameOperations.create": {"executed_lines": [238, 239, 241, 242, 244, 247, 248, 249, 254, 256, 257, 259], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 203}, "DataFrameOperations.update": {"executed_lines": [324, 325, 326, 327, 328, 329, 331, 332, 333, 334, 338, 340, 341, 342, 345, 348, 349, 350, 351, 352, 354, 355, 357], "summary": {"covered_lines": 23, "num_statements": 23, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 263}, "DataFrameOperations.delete": {"executed_lines": [394, 395, 397, 398, 399, 401, 402, 403, 406, 408, 409, 410, 411], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 361}, "": {"executed_lines": [6, 8, 10, 12, 18, 21, 51, 56, 98, 203, 263, 361], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [14, 15], "start_line": 1}}, "classes": {"DataFrameOperations": {"executed_lines": [52, 91, 92, 93, 94, 167, 168, 169, 170, 171, 172, 176, 181, 183, 184, 195, 197, 198, 199, 238, 239, 241, 242, 244, 247, 248, 249, 254, 256, 257, 259, 324, 325, 326, 327, 328, 329, 331, 332, 333, 334, 338, 340, 341, 342, 345, 348, 349, 350, 351, 352, 354, 355, 357, 394, 395, 397, 398, 399, 401, 402, 403, 406, 408, 409, 410, 411], "summary": {"covered_lines": 67, "num_statements": 67, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 21}, "": {"executed_lines": [6, 8, 10, 12, 18, 21, 51, 56, 98, 203, 263, 361], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [14, 15], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\operations\\files.py": {"executed_lines": [6, 8, 14, 17, 35, 36, 40, 104, 105], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [10, 11], "functions": {"FileOperations.__init__": {"executed_lines": [36], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 35}, "FileOperations.upload": {"executed_lines": [104, 105], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 40}, "": {"executed_lines": [6, 8, 14, 17, 35, 40], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [10, 11], "start_line": 1}}, "classes": {"FileOperations": {"executed_lines": [36, 104, 105], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 17}, "": {"executed_lines": [6, 8, 14, 17, 35, 40], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [10, 11], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\operations\\query.py": {"executed_lines": [6, 8, 10, 11, 12, 18, 21, 49, 50, 54, 89, 90, 91, 95, 145, 146, 147, 151, 181, 190, 203, 204, 205, 206, 208, 209, 212, 213, 215, 216, 217, 218, 219, 220, 221, 230, 231, 235, 260, 261, 265, 309, 310, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 330, 331, 332, 333, 334, 335, 336, 337, 339, 349, 350, 354, 391, 392, 393, 394, 395, 400, 401, 402, 403, 411, 436, 437, 441, 478, 479, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 494, 495, 496, 497, 498, 499, 501, 511, 512, 516, 548, 549, 550, 551, 552, 557, 561, 597, 598, 599, 600, 601, 606, 607, 608, 609], "summary": {"covered_lines": 117, "num_statements": 120, "percent_covered": 97.5, "percent_covered_display": "98", "missing_lines": 3, "excluded_lines": 3, "percent_statements_covered": 97.5, "percent_statements_covered_display": "98"}, "missing_lines": [207, 323, 491], "excluded_lines": [14, 15, 445], "functions": {"QueryOperations.__init__": {"executed_lines": [50], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 49}, "QueryOperations.builder": {"executed_lines": [89, 90, 91], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 54}, "QueryOperations.sql": {"executed_lines": [145, 146, 147], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 95}, "QueryOperations.sql_columns": {"executed_lines": [181, 190, 203, 204, 205, 206, 208, 209, 212, 213, 215, 216, 217, 218, 219, 220, 221, 230, 231], "summary": {"covered_lines": 19, "num_statements": 20, "percent_covered": 95.0, "percent_covered_display": "95", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 95.0, "percent_statements_covered_display": "95"}, "missing_lines": [207], "excluded_lines": [], "start_line": 151}, "QueryOperations.sql_select": {"executed_lines": [260, 261], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 235}, "QueryOperations.sql_joins": {"executed_lines": [309, 310, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 330, 331, 332, 333, 334, 335, 336, 337, 339, 349, 350], "summary": {"covered_lines": 24, "num_statements": 25, "percent_covered": 96.0, "percent_covered_display": "96", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 96.0, "percent_statements_covered_display": "96"}, "missing_lines": [323], "excluded_lines": [], "start_line": 265}, "QueryOperations.sql_join": {"executed_lines": [391, 392, 393, 394, 395, 400, 401, 402, 403], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 354}, "QueryOperations.odata_select": {"executed_lines": [436, 437], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 411}, "QueryOperations.odata_expands": {"executed_lines": [478, 479, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 494, 495, 496, 497, 498, 499, 501, 511, 512], "summary": {"covered_lines": 21, "num_statements": 22, "percent_covered": 95.45454545454545, "percent_covered_display": "95", "missing_lines": 1, "excluded_lines": 1, "percent_statements_covered": 95.45454545454545, "percent_statements_covered_display": "95"}, "missing_lines": [491], "excluded_lines": [445], "start_line": 441}, "QueryOperations.odata_expand": {"executed_lines": [548, 549, 550, 551, 552, 557], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 516}, "QueryOperations.odata_bind": {"executed_lines": [597, 598, 599, 600, 601, 606, 607, 608, 609], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 561}, "": {"executed_lines": [6, 8, 10, 11, 12, 18, 21, 49, 54, 95, 151, 235, 265, 354, 411, 441, 516, 561], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [14, 15], "start_line": 1}}, "classes": {"QueryOperations": {"executed_lines": [50, 89, 90, 91, 145, 146, 147, 181, 190, 203, 204, 205, 206, 208, 209, 212, 213, 215, 216, 217, 218, 219, 220, 221, 230, 231, 260, 261, 309, 310, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 330, 331, 332, 333, 334, 335, 336, 337, 339, 349, 350, 391, 392, 393, 394, 395, 400, 401, 402, 403, 436, 437, 478, 479, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 494, 495, 496, 497, 498, 499, 501, 511, 512, 548, 549, 550, 551, 552, 557, 597, 598, 599, 600, 601, 606, 607, 608, 609], "summary": {"covered_lines": 99, "num_statements": 102, "percent_covered": 97.05882352941177, "percent_covered_display": "97", "missing_lines": 3, "excluded_lines": 1, "percent_statements_covered": 97.05882352941177, "percent_statements_covered_display": "97"}, "missing_lines": [207, 323, 491], "excluded_lines": [445], "start_line": 21}, "": {"executed_lines": [6, 8, 10, 11, 12, 18, 21, 49, 54, 95, 151, 235, 265, 354, 411, 441, 516, 561], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [14, 15], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\operations\\records.py": {"executed_lines": [6, 8, 10, 11, 17, 20, 46, 47, 57, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 110, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 174, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 228, 229, 262, 263, 331, 426, 427, 428, 429, 438, 443, 444, 445, 447, 448, 449, 460, 462, 466, 542, 543, 544, 545, 546, 547, 548, 549, 551, 552, 553, 554, 555, 556, 558, 559, 560, 561], "summary": {"covered_lines": 86, "num_statements": 86, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 18, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [13, 14, 51, 52, 53, 54, 55, 56, 168, 169, 170, 171, 172, 173, 260, 261, 329, 330], "functions": {"RecordOperations.__init__": {"executed_lines": [47], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 46}, "RecordOperations.create": {"executed_lines": [94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 57}, "RecordOperations.update": {"executed_lines": [155, 156, 157, 158, 159, 160, 161, 162, 163, 164], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 110}, "RecordOperations.delete": {"executed_lines": [210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 174}, "RecordOperations.get": {"executed_lines": [426, 427, 428, 429, 438, 443, 444, 445, 447, 462], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 331}, "RecordOperations.get._paged": {"executed_lines": [448, 449, 460], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 447}, "RecordOperations.upsert": {"executed_lines": [542, 543, 544, 545, 546, 547, 548, 549, 551, 552, 553, 554, 555, 556, 558, 559, 560, 561], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 466}, "": {"executed_lines": [6, 8, 10, 11, 17, 20, 46, 57, 110, 174, 228, 229, 262, 263, 331, 466], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 12, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [13, 14, 51, 53, 54, 56, 168, 170, 171, 173, 261, 330], "start_line": 1}}, "classes": {"RecordOperations": {"executed_lines": [47, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 426, 427, 428, 429, 438, 443, 444, 445, 447, 448, 449, 460, 462, 542, 543, 544, 545, 546, 547, 548, 549, 551, 552, 553, 554, 555, 556, 558, 559, 560, 561], "summary": {"covered_lines": 70, "num_statements": 70, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 6, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [52, 55, 169, 172, 260, 329], "start_line": 20}, "": {"executed_lines": [6, 8, 10, 11, 17, 20, 46, 57, 110, 174, 228, 229, 262, 263, 331, 466], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 12, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [13, 14, 51, 53, 54, 56, 168, 170, 171, 173, 261, 330], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\operations\\tables.py": {"executed_lines": [6, 8, 10, 17, 18, 19, 20, 26, 29, 65, 66, 70, 135, 136, 143, 147, 164, 165, 169, 189, 190, 191, 192, 193, 197, 245, 246, 250, 278, 279, 283, 311, 312, 316, 380, 381, 386, 396, 436, 437, 441, 450, 469, 470, 474, 493, 494, 495, 496, 497, 501, 567, 568, 579, 583, 634, 635, 636, 637, 646, 668, 669, 670, 674, 698, 699, 703, 749, 750, 754, 793, 794, 798, 838, 839], "summary": {"covered_lines": 75, "num_statements": 75, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [22, 23], "functions": {"TableOperations.__init__": {"executed_lines": [66], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 65}, "TableOperations.create": {"executed_lines": [135, 136, 143], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 70}, "TableOperations.delete": {"executed_lines": [164, 165], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 147}, "TableOperations.get": {"executed_lines": [189, 190, 191, 192, 193], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 169}, "TableOperations.list": {"executed_lines": [245, 246], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 197}, "TableOperations.add_columns": {"executed_lines": [278, 279], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 250}, "TableOperations.remove_columns": {"executed_lines": [311, 312], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 283}, "TableOperations.create_one_to_many_relationship": {"executed_lines": [380, 381, 386], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 316}, "TableOperations.create_many_to_many_relationship": {"executed_lines": [436, 437, 441], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 396}, "TableOperations.delete_relationship": {"executed_lines": [469, 470], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 450}, "TableOperations.get_relationship": {"executed_lines": [493, 494, 495, 496, 497], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 474}, "TableOperations.create_lookup_field": {"executed_lines": [567, 568, 579], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 501}, "TableOperations.create_alternate_key": {"executed_lines": [634, 635, 636, 637], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 583}, "TableOperations.get_alternate_keys": {"executed_lines": [668, 669, 670], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 646}, "TableOperations.delete_alternate_key": {"executed_lines": [698, 699], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 674}, "TableOperations.list_columns": {"executed_lines": [749, 750], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 703}, "TableOperations.list_relationships": {"executed_lines": [793, 794], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 754}, "TableOperations.list_table_relationships": {"executed_lines": [838, 839], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 798}, "": {"executed_lines": [6, 8, 10, 17, 18, 19, 20, 26, 29, 65, 70, 147, 169, 197, 250, 283, 316, 396, 450, 474, 501, 583, 646, 674, 703, 754, 798], "summary": {"covered_lines": 27, "num_statements": 27, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [22, 23], "start_line": 1}}, "classes": {"TableOperations": {"executed_lines": [66, 135, 136, 143, 164, 165, 189, 190, 191, 192, 193, 245, 246, 278, 279, 311, 312, 380, 381, 386, 436, 437, 441, 469, 470, 493, 494, 495, 496, 497, 567, 568, 579, 634, 635, 636, 637, 668, 669, 670, 698, 699, 749, 750, 793, 794, 838, 839], "summary": {"covered_lines": 48, "num_statements": 48, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 29}, "": {"executed_lines": [6, 8, 10, 17, 18, 19, 20, 26, 29, 65, 70, 147, 169, 197, 250, 283, 316, 396, 450, 474, 501, 583, 646, 674, 703, 754, 798], "summary": {"covered_lines": 27, "num_statements": 27, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [22, 23], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\utils\\__init__.py": {"executed_lines": [10], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [10], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [10], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\utils\\_pandas.py": {"executed_lines": [6, 8, 9, 11, 12, 15, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 36, 43, 44, 45, 46, 47, 48, 49, 50, 51, 55, 56, 58, 59, 60], "summary": {"covered_lines": 34, "num_statements": 34, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"_normalize_scalar": {"executed_lines": [21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 15}, "dataframe_to_records": {"executed_lines": [43, 44, 45, 46, 47, 48, 49, 50, 51, 55, 56, 58, 59, 60], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 36}, "": {"executed_lines": [6, 8, 9, 11, 12, 15, 36], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [6, 8, 9, 11, 12, 15, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 36, 43, 44, 45, 46, 47, 48, 49, 50, 51, 55, 56, 58, 59, 60], "summary": {"covered_lines": 34, "num_statements": 34, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\__init__.py": {"executed_lines": [4], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [4], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [4], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}}, "totals": {"covered_lines": 4692, "num_statements": 4956, "percent_covered": 94.67312348668281, "percent_covered_display": "95", "missing_lines": 264, "excluded_lines": 71, "percent_statements_covered": 94.67312348668281, "percent_statements_covered_display": "95"}} \ No newline at end of file diff --git a/coverage.xml b/coverage.xml deleted file mode 100644 index e3dcc7ce..00000000 --- a/coverage.xml +++ /dev/null @@ -1,3181 +0,0 @@ - - - - - - C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/coverage_aio.json b/coverage_aio.json deleted file mode 100644 index b75e36fa..00000000 --- a/coverage_aio.json +++ /dev/null @@ -1 +0,0 @@ -{"meta": {"format": 3, "version": "7.13.5", "timestamp": "2026-04-27T21:26:52.956879", "branch_coverage": false, "show_contexts": false}, "files": {"src\\PowerPlatform\\Dataverse\\__init__.py": {"executed_lines": [4, 6, 8], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [4, 6, 8], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [4, 6, 8], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\_skill_installer.py": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 139, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 139, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [13, 14, 15, 18, 24, 28, 29, 31, 34, 35, 36, 40, 41, 42, 44, 47, 49, 50, 56, 66, 67, 70, 71, 73, 74, 77, 78, 79, 80, 81, 83, 84, 85, 86, 87, 88, 91, 92, 93, 94, 95, 96, 97, 98, 101, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 129, 132, 139, 141, 142, 144, 145, 146, 147, 149, 150, 151, 152, 153, 154, 155, 156, 158, 159, 160, 162, 163, 164, 167, 169, 171, 172, 174, 175, 176, 177, 179, 180, 181, 182, 183, 184, 185, 187, 188, 190, 191, 193, 194, 195, 196, 197, 198, 199, 201, 202, 205, 207, 209, 238, 242, 244, 246, 248, 249, 250, 251, 254, 255, 256, 258, 259, 260, 263, 264, 267, 268], "excluded_lines": [], "functions": {"get_skill_source_paths": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 11, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 11, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [24, 28, 29, 31, 34, 35, 36, 40, 41, 42, 44], "excluded_lines": [], "start_line": 18}, "get_skill_destination_paths": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [49, 50], "excluded_lines": [], "start_line": 47}, "install_skill": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 49, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 49, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [66, 67, 70, 71, 73, 74, 77, 78, 79, 80, 81, 83, 84, 85, 86, 87, 88, 91, 92, 93, 94, 95, 96, 97, 98, 101, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 129], "excluded_lines": [], "start_line": 56}, "uninstall_skill": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 21, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 21, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [139, 141, 142, 144, 145, 146, 147, 149, 150, 151, 152, 153, 154, 155, 156, 158, 159, 160, 162, 163, 164], "excluded_lines": [], "start_line": 132}, "check_skill_status": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 27, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 27, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [169, 171, 172, 174, 175, 176, 177, 179, 180, 181, 182, 183, 184, 185, 187, 188, 190, 191, 193, 194, 195, 196, 197, 198, 199, 201, 202], "excluded_lines": [], "start_line": 167}, "main": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 18, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 18, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [207, 209, 238, 242, 244, 246, 248, 249, 250, 251, 254, 255, 256, 258, 259, 260, 263, 264], "excluded_lines": [], "start_line": 205}, "": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 11, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 11, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [13, 14, 15, 18, 47, 56, 132, 167, 205, 267, 268], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 139, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 139, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [13, 14, 15, 18, 24, 28, 29, 31, 34, 35, 36, 40, 41, 42, 44, 47, 49, 50, 56, 66, 67, 70, 71, 73, 74, 77, 78, 79, 80, 81, 83, 84, 85, 86, 87, 88, 91, 92, 93, 94, 95, 96, 97, 98, 101, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 129, 132, 139, 141, 142, 144, 145, 146, 147, 149, 150, 151, 152, 153, 154, 155, 156, 158, 159, 160, 162, 163, 164, 167, 169, 171, 172, 174, 175, 176, 177, 179, 180, 181, 182, 183, 184, 185, 187, 188, 190, 191, 193, 194, 195, 196, 197, 198, 199, 201, 202, 205, 207, 209, 238, 242, 244, 246, 248, 249, 250, 251, 254, 255, 256, 258, 259, 260, 263, 264, 267, 268], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\__init__.py": {"executed_lines": [4, 6], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [4, 6], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [4, 6], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\async_client.py": {"executed_lines": [4, 6, 7, 9, 10, 12, 13, 14, 15, 16, 17, 18, 19, 20, 23, 93, 99, 100, 101, 102, 103, 104, 105, 106, 109, 110, 111, 112, 113, 114, 116, 135, 136, 146, 158, 159, 160, 161, 163, 169, 171, 188, 189, 190, 191, 192, 193, 194, 195, 196, 198, 200, 201], "summary": {"covered_lines": 53, "num_statements": 60, "percent_covered": 88.33333333333333, "percent_covered_display": "88", "missing_lines": 7, "excluded_lines": 0, "percent_statements_covered": 88.33333333333333, "percent_statements_covered_display": "88"}, "missing_lines": [126, 127, 133, 138, 139, 141, 142], "excluded_lines": [], "functions": {"AsyncDataverseClient.__init__": {"executed_lines": [99, 100, 101, 102, 103, 104, 105, 106, 109, 110, 111, 112, 113, 114], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 93}, "AsyncDataverseClient._get_odata": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 3, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [126, 127, 133], "excluded_lines": [], "start_line": 116}, "AsyncDataverseClient._scoped_odata": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 4, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [138, 139, 141, 142], "excluded_lines": [], "start_line": 136}, "AsyncDataverseClient.__aenter__": {"executed_lines": [158, 159, 160, 161], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 146}, "AsyncDataverseClient.__aexit__": {"executed_lines": [169], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 163}, "AsyncDataverseClient.aclose": {"executed_lines": [188, 189, 190, 191, 192, 193, 194, 195, 196], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 171}, "AsyncDataverseClient._check_closed": {"executed_lines": [200, 201], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 198}, "": {"executed_lines": [4, 6, 7, 9, 10, 12, 13, 14, 15, 16, 17, 18, 19, 20, 23, 93, 116, 135, 136, 146, 163, 171, 198], "summary": {"covered_lines": 23, "num_statements": 23, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"AsyncDataverseClient": {"executed_lines": [99, 100, 101, 102, 103, 104, 105, 106, 109, 110, 111, 112, 113, 114, 158, 159, 160, 161, 169, 188, 189, 190, 191, 192, 193, 194, 195, 196, 200, 201], "summary": {"covered_lines": 30, "num_statements": 37, "percent_covered": 81.08108108108108, "percent_covered_display": "81", "missing_lines": 7, "excluded_lines": 0, "percent_statements_covered": 81.08108108108108, "percent_statements_covered_display": "81"}, "missing_lines": [126, 127, 133, 138, 139, 141, 142], "excluded_lines": [], "start_line": 23}, "": {"executed_lines": [4, 6, 7, 9, 10, 12, 13, 14, 15, 16, 17, 18, 19, 20, 23, 93, 116, 135, 136, 146, 163, 171, 198], "summary": {"covered_lines": 23, "num_statements": 23, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\core\\__init__.py": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\core\\_async_auth.py": {"executed_lines": [13, 15, 17, 20, 29, 30, 31, 32, 34, 44, 45], "summary": {"covered_lines": 11, "num_statements": 11, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"_AsyncAuthManager.__init__": {"executed_lines": [30, 31, 32], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 29}, "_AsyncAuthManager._acquire_token": {"executed_lines": [44, 45], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 34}, "": {"executed_lines": [13, 15, 17, 20, 29, 34], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_AsyncAuthManager": {"executed_lines": [30, 31, 32, 44, 45], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 20}, "": {"executed_lines": [13, 15, 17, 20, 29, 34], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\core\\_async_http.py": {"executed_lines": [12, 14, 15, 16, 18, 24, 47, 55, 56, 57, 58, 59, 61, 82, 83, 87, 88, 89, 91, 92, 93, 97, 98, 100, 103, 104, 112, 113, 114, 115, 118, 119, 121, 124, 125, 133, 134, 135, 136, 143, 144, 145, 146, 147, 149, 154, 155, 156], "summary": {"covered_lines": 48, "num_statements": 50, "percent_covered": 96.0, "percent_covered_display": "96", "missing_lines": 2, "excluded_lines": 2, "percent_statements_covered": 96.0, "percent_statements_covered_display": "96"}, "missing_lines": [99, 101], "excluded_lines": [20, 21], "functions": {"_AsyncHttpClient.__init__": {"executed_lines": [55, 56, 57, 58, 59], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 47}, "_AsyncHttpClient._request": {"executed_lines": [82, 83, 87, 88, 89, 91, 92, 93, 97, 98, 100, 103, 104, 112, 113, 114, 115, 118, 119, 121, 124, 125, 133, 134, 135, 136, 143, 144, 145, 146, 147], "summary": {"covered_lines": 31, "num_statements": 33, "percent_covered": 93.93939393939394, "percent_covered_display": "94", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 93.93939393939394, "percent_statements_covered_display": "94"}, "missing_lines": [99, 101], "excluded_lines": [], "start_line": 61}, "_AsyncHttpClient.close": {"executed_lines": [154, 155, 156], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 149}, "": {"executed_lines": [12, 14, 15, 16, 18, 24, 47, 61, 149], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [20, 21], "start_line": 1}}, "classes": {"_AsyncHttpClient": {"executed_lines": [55, 56, 57, 58, 59, 82, 83, 87, 88, 89, 91, 92, 93, 97, 98, 100, 103, 104, 112, 113, 114, 115, 118, 119, 121, 124, 125, 133, 134, 135, 136, 143, 144, 145, 146, 147, 154, 155, 156], "summary": {"covered_lines": 39, "num_statements": 41, "percent_covered": 95.1219512195122, "percent_covered_display": "95", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 95.1219512195122, "percent_statements_covered_display": "95"}, "missing_lines": [99, 101], "excluded_lines": [], "start_line": 24}, "": {"executed_lines": [12, 14, 15, 16, 18, 24, 47, 61, 149], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [20, 21], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\data\\__init__.py": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\data\\_async_batch.py": {"executed_lines": [6, 8, 9, 11, 12, 13, 14, 15, 42, 45, 61, 67, 76, 90, 158, 173, 214, 228, 234, 242, 255, 258, 273, 283, 287, 291, 311], "summary": {"covered_lines": 27, "num_statements": 149, "percent_covered": 18.120805369127517, "percent_covered_display": "18", "missing_lines": 122, "excluded_lines": 2, "percent_statements_covered": 18.120805369127517, "percent_statements_covered_display": "18"}, "missing_lines": [62, 63, 64, 65, 68, 101, 102, 104, 106, 107, 108, 115, 116, 118, 121, 122, 124, 125, 140, 141, 142, 143, 144, 146, 152, 159, 160, 161, 162, 164, 165, 166, 167, 168, 170, 171, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 216, 217, 218, 222, 229, 230, 231, 232, 235, 236, 237, 238, 239, 240, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 256, 259, 260, 261, 262, 263, 264, 265, 275, 276, 277, 281, 284, 285, 288, 289, 292, 293, 294, 295, 296, 299, 300, 304, 305, 312], "excluded_lines": [39, 40], "functions": {"_SyncResponseWrapper.__init__": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 4, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [62, 63, 64, 65], "excluded_lines": [], "start_line": 61}, "_SyncResponseWrapper.json": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [68], "excluded_lines": [], "start_line": 67}, "_AsyncBatchClient.execute": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 20, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 20, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [101, 102, 104, 106, 107, 108, 115, 116, 118, 121, 122, 124, 125, 140, 141, 142, 143, 144, 146, 152], "excluded_lines": [], "start_line": 90}, "_AsyncBatchClient._resolve_all": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 11, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 11, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [159, 160, 161, 162, 164, 165, 166, 167, 168, 170, 171], "excluded_lines": [], "start_line": 158}, "_AsyncBatchClient._resolve_item": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 35, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 35, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209], "excluded_lines": [], "start_line": 173}, "_AsyncBatchClient._resolve_one": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 4, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [216, 217, 218, 222], "excluded_lines": [], "start_line": 214}, "_AsyncBatchClient._resolve_record_create": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 4, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [229, 230, 231, 232], "excluded_lines": [], "start_line": 228}, "_AsyncBatchClient._resolve_record_update": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 6, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 6, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [235, 236, 237, 238, 239, 240], "excluded_lines": [], "start_line": 234}, "_AsyncBatchClient._resolve_record_delete": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 11, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 11, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253], "excluded_lines": [], "start_line": 242}, "_AsyncBatchClient._resolve_record_get": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [256], "excluded_lines": [], "start_line": 255}, "_AsyncBatchClient._resolve_record_upsert": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 7, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 7, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [259, 260, 261, 262, 263, 264, 265], "excluded_lines": [], "start_line": 258}, "_AsyncBatchClient._require_entity_metadata": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 4, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [275, 276, 277, 281], "excluded_lines": [], "start_line": 273}, "_AsyncBatchClient._resolve_table_delete": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [284, 285], "excluded_lines": [], "start_line": 283}, "_AsyncBatchClient._resolve_table_add_columns": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [288, 289], "excluded_lines": [], "start_line": 287}, "_AsyncBatchClient._resolve_table_remove_columns": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 9, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 9, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [292, 293, 294, 295, 296, 299, 300, 304, 305], "excluded_lines": [], "start_line": 291}, "_AsyncBatchClient._resolve_query_sql": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [312], "excluded_lines": [], "start_line": 311}, "": {"executed_lines": [6, 8, 9, 11, 12, 13, 14, 15, 42, 45, 61, 67, 76, 90, 158, 173, 214, 228, 234, 242, 255, 258, 273, 283, 287, 291, 311], "summary": {"covered_lines": 27, "num_statements": 27, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [39, 40], "start_line": 1}}, "classes": {"_SyncResponseWrapper": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 5, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 5, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [62, 63, 64, 65, 68], "excluded_lines": [], "start_line": 45}, "_AsyncBatchClient": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 117, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 117, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [101, 102, 104, 106, 107, 108, 115, 116, 118, 121, 122, 124, 125, 140, 141, 142, 143, 144, 146, 152, 159, 160, 161, 162, 164, 165, 166, 167, 168, 170, 171, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 216, 217, 218, 222, 229, 230, 231, 232, 235, 236, 237, 238, 239, 240, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 256, 259, 260, 261, 262, 263, 264, 265, 275, 276, 277, 281, 284, 285, 288, 289, 292, 293, 294, 295, 296, 299, 300, 304, 305, 312], "excluded_lines": [], "start_line": 76}, "": {"executed_lines": [6, 8, 9, 11, 12, 13, 14, 15, 42, 45, 61, 67, 76, 90, 158, 173, 214, 228, 234, 242, 255, 258, 273, 283, 287, 291, 311], "summary": {"covered_lines": 27, "num_statements": 27, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [39, 40], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\data\\_async_odata.py": {"executed_lines": [6, 8, 10, 11, 12, 13, 14, 15, 17, 19, 21, 22, 23, 24, 25, 26, 39, 49, 52, 83, 93, 106, 109, 199, 218, 250, 299, 331, 388, 403, 443, 474, 488, 516, 529, 550, 642, 759, 810, 833, 869, 905, 949, 983, 998, 1052, 1096, 1118, 1148, 1170, 1221, 1247, 1274, 1358, 1414, 1488, 1506, 1529, 1557, 1583, 1605, 1627, 1666, 1689, 1731, 1745], "summary": {"covered_lines": 66, "num_statements": 699, "percent_covered": 9.44206008583691, "percent_covered_display": "9", "missing_lines": 633, "excluded_lines": 0, "percent_statements_covered": 9.44206008583691, "percent_statements_covered_display": "9"}, "missing_lines": [73, 74, 75, 89, 90, 91, 95, 96, 97, 107, 120, 122, 123, 124, 125, 126, 127, 129, 137, 138, 139, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 160, 161, 162, 163, 164, 165, 166, 167, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 210, 211, 212, 213, 214, 215, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 271, 272, 273, 274, 275, 276, 277, 278, 279, 281, 282, 283, 285, 286, 288, 289, 290, 292, 293, 294, 295, 296, 297, 325, 326, 327, 328, 329, 362, 363, 367, 368, 369, 370, 371, 372, 373, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 390, 391, 392, 393, 395, 396, 397, 398, 399, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 458, 459, 460, 461, 465, 466, 467, 468, 469, 470, 471, 472, 486, 511, 512, 513, 514, 527, 547, 548, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 597, 598, 599, 600, 601, 602, 603, 605, 606, 607, 608, 610, 611, 613, 614, 616, 617, 619, 620, 621, 622, 623, 625, 626, 627, 628, 630, 631, 632, 634, 635, 636, 637, 638, 639, 661, 662, 663, 664, 665, 670, 672, 673, 674, 675, 676, 679, 680, 681, 682, 683, 685, 686, 687, 690, 691, 692, 693, 694, 696, 697, 706, 707, 711, 712, 713, 714, 723, 724, 725, 726, 727, 728, 735, 736, 737, 738, 739, 746, 747, 748, 749, 750, 751, 752, 753, 754, 756, 764, 765, 768, 769, 770, 771, 772, 774, 775, 776, 780, 781, 782, 783, 784, 785, 786, 787, 792, 796, 797, 798, 799, 803, 804, 805, 806, 807, 821, 823, 824, 825, 829, 830, 831, 840, 841, 853, 854, 855, 856, 857, 861, 862, 865, 866, 867, 876, 877, 878, 879, 880, 881, 882, 883, 884, 885, 886, 887, 888, 889, 893, 894, 895, 896, 897, 898, 899, 900, 901, 902, 903, 933, 934, 935, 939, 940, 941, 942, 943, 944, 945, 946, 947, 962, 963, 964, 965, 967, 968, 969, 970, 971, 972, 973, 974, 975, 978, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 1006, 1007, 1008, 1009, 1010, 1012, 1013, 1018, 1019, 1020, 1022, 1023, 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1034, 1035, 1036, 1037, 1038, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1050, 1061, 1064, 1068, 1069, 1072, 1075, 1076, 1077, 1078, 1079, 1081, 1082, 1083, 1084, 1085, 1086, 1087, 1088, 1089, 1090, 1091, 1092, 1093, 1094, 1105, 1106, 1107, 1108, 1145, 1146, 1160, 1161, 1162, 1166, 1197, 1198, 1199, 1204, 1205, 1206, 1210, 1211, 1212, 1213, 1215, 1235, 1236, 1237, 1242, 1243, 1244, 1245, 1263, 1264, 1265, 1270, 1271, 1272, 1304, 1305, 1306, 1311, 1315, 1316, 1318, 1322, 1323, 1324, 1325, 1326, 1327, 1328, 1329, 1331, 1332, 1333, 1334, 1335, 1337, 1338, 1339, 1341, 1348, 1378, 1379, 1381, 1382, 1383, 1388, 1389, 1390, 1392, 1393, 1394, 1395, 1399, 1400, 1401, 1406, 1407, 1409, 1410, 1412, 1435, 1436, 1437, 1438, 1440, 1442, 1443, 1444, 1446, 1447, 1448, 1454, 1455, 1456, 1457, 1459, 1460, 1461, 1462, 1467, 1468, 1469, 1471, 1473, 1474, 1475, 1476, 1477, 1479, 1481, 1482, 1484, 1497, 1498, 1499, 1513, 1514, 1515, 1516, 1517, 1518, 1519, 1520, 1521, 1522, 1523, 1542, 1543, 1544, 1545, 1547, 1548, 1549, 1569, 1570, 1571, 1572, 1573, 1574, 1575, 1576, 1577, 1591, 1592, 1593, 1594, 1595, 1596, 1600, 1602, 1603, 1617, 1618, 1619, 1620, 1621, 1635, 1636, 1641, 1642, 1643, 1644, 1645, 1646, 1647, 1650, 1651, 1655, 1656, 1657, 1658, 1659, 1660, 1677, 1678, 1680, 1681, 1682, 1691, 1692, 1693, 1694, 1725, 1739, 1740, 1741, 1742, 1743, 1760, 1761, 1762], "excluded_lines": [], "functions": {"_AsyncODataClient.__init__": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 3, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [73, 74, 75], "excluded_lines": [], "start_line": 52}, "_AsyncODataClient.close": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 3, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [89, 90, 91], "excluded_lines": [], "start_line": 83}, "_AsyncODataClient._headers": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 3, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [95, 96, 97], "excluded_lines": [], "start_line": 93}, "_AsyncODataClient._raw_request": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [107], "excluded_lines": [], "start_line": 106}, "_AsyncODataClient._request": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 42, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 42, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [120, 122, 129, 137, 138, 139, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 160, 161, 162, 163, 164, 165, 166, 167, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181], "excluded_lines": [], "start_line": 109}, "_AsyncODataClient._request._merge": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 5, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 5, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [123, 124, 125, 126, 127], "excluded_lines": [], "start_line": 122}, "_AsyncODataClient._execute_raw": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 6, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 6, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [210, 211, 212, 213, 214, 215], "excluded_lines": [], "start_line": 199}, "_AsyncODataClient._create": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 13, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 13, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246], "excluded_lines": [], "start_line": 218}, "_AsyncODataClient._create_multiple": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 23, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 23, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [271, 272, 273, 274, 275, 276, 277, 278, 279, 281, 282, 283, 285, 286, 288, 289, 290, 292, 293, 294, 295, 296, 297], "excluded_lines": [], "start_line": 250}, "_AsyncODataClient._upsert": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 5, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 5, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [325, 326, 327, 328, 329], "excluded_lines": [], "start_line": 299}, "_AsyncODataClient._upsert_multiple": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 19, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 19, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [362, 363, 367, 368, 369, 370, 371, 372, 373, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385], "excluded_lines": [], "start_line": 331}, "_AsyncODataClient._primary_id_attr": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 9, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 9, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [390, 391, 392, 393, 395, 396, 397, 398, 399], "excluded_lines": [], "start_line": 388}, "_AsyncODataClient._update_by_ids": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 21, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 21, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441], "excluded_lines": [], "start_line": 403}, "_AsyncODataClient._delete_multiple": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 12, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 12, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [458, 459, 460, 461, 465, 466, 467, 468, 469, 470, 471, 472], "excluded_lines": [], "start_line": 443}, "_AsyncODataClient._update": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [486], "excluded_lines": [], "start_line": 474}, "_AsyncODataClient._update_multiple": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 4, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [511, 512, 513, 514], "excluded_lines": [], "start_line": 488}, "_AsyncODataClient._delete": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [527], "excluded_lines": [], "start_line": 516}, "_AsyncODataClient._get": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [547, 548], "excluded_lines": [], "start_line": 529}, "_AsyncODataClient._get_multiple": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 39, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 39, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 597, 605, 606, 607, 608, 610, 611, 613, 614, 616, 617, 619, 620, 621, 622, 623, 625, 626, 627, 628, 630, 631, 632, 634, 635, 636, 637, 638, 639], "excluded_lines": [], "start_line": 550}, "_AsyncODataClient._get_multiple._do_request": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 6, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 6, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [598, 599, 600, 601, 602, 603], "excluded_lines": [], "start_line": 597}, "_AsyncODataClient._query_sql": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 53, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 53, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [661, 662, 663, 664, 665, 670, 672, 673, 674, 675, 676, 679, 680, 681, 682, 683, 685, 686, 687, 690, 691, 692, 693, 694, 696, 697, 706, 707, 711, 712, 713, 714, 723, 724, 725, 726, 727, 728, 735, 736, 737, 738, 739, 746, 747, 748, 749, 750, 751, 752, 753, 754, 756], "excluded_lines": [], "start_line": 642}, "_AsyncODataClient._entity_set_from_schema_name": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 28, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 28, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [764, 765, 768, 769, 770, 771, 772, 774, 775, 776, 780, 781, 782, 783, 784, 785, 786, 787, 792, 796, 797, 798, 799, 803, 804, 805, 806, 807], "excluded_lines": [], "start_line": 759}, "_AsyncODataClient._get_entity_by_table_schema_name": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 7, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 7, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [821, 823, 824, 825, 829, 830, 831], "excluded_lines": [], "start_line": 810}, "_AsyncODataClient._create_entity": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 12, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 12, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [840, 841, 853, 854, 855, 856, 857, 861, 862, 865, 866, 867], "excluded_lines": [], "start_line": 833}, "_AsyncODataClient._get_attribute_metadata": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 25, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 25, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [876, 877, 878, 879, 880, 881, 882, 883, 884, 885, 886, 887, 888, 889, 893, 894, 895, 896, 897, 898, 899, 900, 901, 902, 903], "excluded_lines": [], "start_line": 869}, "_AsyncODataClient._list_columns": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 12, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 12, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [933, 934, 935, 939, 940, 941, 942, 943, 944, 945, 946, 947], "excluded_lines": [], "start_line": 905}, "_AsyncODataClient._wait_for_attribute_visibility": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 14, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 14, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [962, 963, 964, 965, 967, 968, 969, 970, 971, 972, 973, 974, 975, 978], "excluded_lines": [], "start_line": 949}, "_AsyncODataClient._request_metadata_with_retry": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 12, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 12, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996], "excluded_lines": [], "start_line": 983}, "_AsyncODataClient._bulk_fetch_picklists": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 38, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 38, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1006, 1007, 1008, 1009, 1010, 1012, 1013, 1018, 1019, 1020, 1022, 1023, 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1034, 1035, 1036, 1037, 1038, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1050], "excluded_lines": [], "start_line": 998}, "_AsyncODataClient._convert_labels_to_ints": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 24, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 24, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1061, 1064, 1068, 1069, 1072, 1075, 1076, 1077, 1078, 1079, 1081, 1082, 1083, 1084, 1085, 1086, 1087, 1088, 1089, 1090, 1091, 1092, 1093, 1094], "excluded_lines": [], "start_line": 1052}, "_AsyncODataClient._get_table_info": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 4, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1105, 1106, 1107, 1108], "excluded_lines": [], "start_line": 1096}, "_AsyncODataClient._list_tables": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1145, 1146], "excluded_lines": [], "start_line": 1118}, "_AsyncODataClient._delete_table": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 4, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1160, 1161, 1162, 1166], "excluded_lines": [], "start_line": 1148}, "_AsyncODataClient._create_alternate_key": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 11, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 11, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1197, 1198, 1199, 1204, 1205, 1206, 1210, 1211, 1212, 1213, 1215], "excluded_lines": [], "start_line": 1170}, "_AsyncODataClient._get_alternate_keys": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 7, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 7, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1235, 1236, 1237, 1242, 1243, 1244, 1245], "excluded_lines": [], "start_line": 1221}, "_AsyncODataClient._delete_alternate_key": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 6, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 6, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1263, 1264, 1265, 1270, 1271, 1272], "excluded_lines": [], "start_line": 1247}, "_AsyncODataClient._create_table": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 25, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 25, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1304, 1305, 1306, 1311, 1315, 1316, 1318, 1322, 1323, 1324, 1325, 1326, 1327, 1328, 1329, 1331, 1332, 1333, 1334, 1335, 1337, 1338, 1339, 1341, 1348], "excluded_lines": [], "start_line": 1274}, "_AsyncODataClient._create_columns": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 20, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 20, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1378, 1379, 1381, 1382, 1383, 1388, 1389, 1390, 1392, 1393, 1394, 1395, 1399, 1400, 1401, 1406, 1407, 1409, 1410, 1412], "excluded_lines": [], "start_line": 1358}, "_AsyncODataClient._delete_columns": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 32, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 32, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1435, 1436, 1437, 1438, 1440, 1442, 1443, 1444, 1446, 1447, 1448, 1454, 1455, 1456, 1457, 1459, 1460, 1461, 1462, 1467, 1468, 1469, 1471, 1473, 1474, 1475, 1476, 1477, 1479, 1481, 1482, 1484], "excluded_lines": [], "start_line": 1414}, "_AsyncODataClient._build_create": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 3, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1497, 1498, 1499], "excluded_lines": [], "start_line": 1488}, "_AsyncODataClient._build_create_multiple": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 11, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 11, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1513, 1514, 1515, 1516, 1517, 1518, 1519, 1520, 1521, 1522, 1523], "excluded_lines": [], "start_line": 1506}, "_AsyncODataClient._build_update": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 7, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 7, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1542, 1543, 1544, 1545, 1547, 1548, 1549], "excluded_lines": [], "start_line": 1529}, "_AsyncODataClient._build_update_multiple_from_records": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 9, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 9, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1569, 1570, 1571, 1572, 1573, 1574, 1575, 1576, 1577], "excluded_lines": [], "start_line": 1557}, "_AsyncODataClient._build_update_multiple": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 9, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 9, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1591, 1592, 1593, 1594, 1595, 1596, 1600, 1602, 1603], "excluded_lines": [], "start_line": 1583}, "_AsyncODataClient._build_upsert": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 5, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 5, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1617, 1618, 1619, 1620, 1621], "excluded_lines": [], "start_line": 1605}, "_AsyncODataClient._build_upsert_multiple": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 17, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 17, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1635, 1636, 1641, 1642, 1643, 1644, 1645, 1646, 1647, 1650, 1651, 1655, 1656, 1657, 1658, 1659, 1660], "excluded_lines": [], "start_line": 1627}, "_AsyncODataClient._build_delete": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 5, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 5, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1677, 1678, 1680, 1681, 1682], "excluded_lines": [], "start_line": 1666}, "_AsyncODataClient._build_delete_multiple": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 5, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 5, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1691, 1692, 1693, 1694, 1725], "excluded_lines": [], "start_line": 1689}, "_AsyncODataClient._build_get": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 5, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 5, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1739, 1740, 1741, 1742, 1743], "excluded_lines": [], "start_line": 1731}, "_AsyncODataClient._build_sql": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 3, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1760, 1761, 1762], "excluded_lines": [], "start_line": 1745}, "": {"executed_lines": [6, 8, 10, 11, 12, 13, 14, 15, 17, 19, 21, 22, 23, 24, 25, 26, 39, 49, 52, 83, 93, 106, 109, 199, 218, 250, 299, 331, 388, 403, 443, 474, 488, 516, 529, 550, 642, 759, 810, 833, 869, 905, 949, 983, 998, 1052, 1096, 1118, 1148, 1170, 1221, 1247, 1274, 1358, 1414, 1488, 1506, 1529, 1557, 1583, 1605, 1627, 1666, 1689, 1731, 1745], "summary": {"covered_lines": 66, "num_statements": 66, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_AsyncODataClient": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 633, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 633, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [73, 74, 75, 89, 90, 91, 95, 96, 97, 107, 120, 122, 123, 124, 125, 126, 127, 129, 137, 138, 139, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 160, 161, 162, 163, 164, 165, 166, 167, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 210, 211, 212, 213, 214, 215, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 271, 272, 273, 274, 275, 276, 277, 278, 279, 281, 282, 283, 285, 286, 288, 289, 290, 292, 293, 294, 295, 296, 297, 325, 326, 327, 328, 329, 362, 363, 367, 368, 369, 370, 371, 372, 373, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 390, 391, 392, 393, 395, 396, 397, 398, 399, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 458, 459, 460, 461, 465, 466, 467, 468, 469, 470, 471, 472, 486, 511, 512, 513, 514, 527, 547, 548, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 597, 598, 599, 600, 601, 602, 603, 605, 606, 607, 608, 610, 611, 613, 614, 616, 617, 619, 620, 621, 622, 623, 625, 626, 627, 628, 630, 631, 632, 634, 635, 636, 637, 638, 639, 661, 662, 663, 664, 665, 670, 672, 673, 674, 675, 676, 679, 680, 681, 682, 683, 685, 686, 687, 690, 691, 692, 693, 694, 696, 697, 706, 707, 711, 712, 713, 714, 723, 724, 725, 726, 727, 728, 735, 736, 737, 738, 739, 746, 747, 748, 749, 750, 751, 752, 753, 754, 756, 764, 765, 768, 769, 770, 771, 772, 774, 775, 776, 780, 781, 782, 783, 784, 785, 786, 787, 792, 796, 797, 798, 799, 803, 804, 805, 806, 807, 821, 823, 824, 825, 829, 830, 831, 840, 841, 853, 854, 855, 856, 857, 861, 862, 865, 866, 867, 876, 877, 878, 879, 880, 881, 882, 883, 884, 885, 886, 887, 888, 889, 893, 894, 895, 896, 897, 898, 899, 900, 901, 902, 903, 933, 934, 935, 939, 940, 941, 942, 943, 944, 945, 946, 947, 962, 963, 964, 965, 967, 968, 969, 970, 971, 972, 973, 974, 975, 978, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 1006, 1007, 1008, 1009, 1010, 1012, 1013, 1018, 1019, 1020, 1022, 1023, 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1034, 1035, 1036, 1037, 1038, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1050, 1061, 1064, 1068, 1069, 1072, 1075, 1076, 1077, 1078, 1079, 1081, 1082, 1083, 1084, 1085, 1086, 1087, 1088, 1089, 1090, 1091, 1092, 1093, 1094, 1105, 1106, 1107, 1108, 1145, 1146, 1160, 1161, 1162, 1166, 1197, 1198, 1199, 1204, 1205, 1206, 1210, 1211, 1212, 1213, 1215, 1235, 1236, 1237, 1242, 1243, 1244, 1245, 1263, 1264, 1265, 1270, 1271, 1272, 1304, 1305, 1306, 1311, 1315, 1316, 1318, 1322, 1323, 1324, 1325, 1326, 1327, 1328, 1329, 1331, 1332, 1333, 1334, 1335, 1337, 1338, 1339, 1341, 1348, 1378, 1379, 1381, 1382, 1383, 1388, 1389, 1390, 1392, 1393, 1394, 1395, 1399, 1400, 1401, 1406, 1407, 1409, 1410, 1412, 1435, 1436, 1437, 1438, 1440, 1442, 1443, 1444, 1446, 1447, 1448, 1454, 1455, 1456, 1457, 1459, 1460, 1461, 1462, 1467, 1468, 1469, 1471, 1473, 1474, 1475, 1476, 1477, 1479, 1481, 1482, 1484, 1497, 1498, 1499, 1513, 1514, 1515, 1516, 1517, 1518, 1519, 1520, 1521, 1522, 1523, 1542, 1543, 1544, 1545, 1547, 1548, 1549, 1569, 1570, 1571, 1572, 1573, 1574, 1575, 1576, 1577, 1591, 1592, 1593, 1594, 1595, 1596, 1600, 1602, 1603, 1617, 1618, 1619, 1620, 1621, 1635, 1636, 1641, 1642, 1643, 1644, 1645, 1646, 1647, 1650, 1651, 1655, 1656, 1657, 1658, 1659, 1660, 1677, 1678, 1680, 1681, 1682, 1691, 1692, 1693, 1694, 1725, 1739, 1740, 1741, 1742, 1743, 1760, 1761, 1762], "excluded_lines": [], "start_line": 49}, "": {"executed_lines": [6, 8, 10, 11, 12, 13, 14, 15, 17, 19, 21, 22, 23, 24, 25, 26, 39, 49, 52, 83, 93, 106, 109, 199, 218, 250, 299, 331, 388, 403, 443, 474, 488, 516, 529, 550, 642, 759, 810, 833, 869, 905, 949, 983, 998, 1052, 1096, 1118, 1148, 1170, 1221, 1247, 1274, 1358, 1414, 1488, 1506, 1529, 1557, 1583, 1605, 1627, 1666, 1689, 1731, 1745], "summary": {"covered_lines": 66, "num_statements": 66, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\data\\_async_relationships.py": {"executed_lines": [10, 12, 14, 15, 18, 28, 74, 114, 128, 147, 180, 250], "summary": {"covered_lines": 12, "num_statements": 71, "percent_covered": 16.901408450704224, "percent_covered_display": "17", "missing_lines": 59, "excluded_lines": 0, "percent_statements_covered": 16.901408450704224, "percent_statements_covered_display": "17"}, "missing_lines": [51, 54, 55, 57, 58, 59, 61, 64, 66, 94, 96, 98, 99, 100, 102, 105, 107, 123, 124, 125, 126, 140, 141, 142, 143, 144, 145, 171, 172, 173, 174, 175, 176, 177, 178, 210, 211, 213, 214, 215, 220, 228, 229, 230, 231, 232, 233, 234, 236, 237, 238, 240, 241, 242, 244, 260, 261, 262, 263], "excluded_lines": [], "functions": {"_AsyncRelationshipOperationsMixin._create_one_to_many_relationship": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 9, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 9, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [51, 54, 55, 57, 58, 59, 61, 64, 66], "excluded_lines": [], "start_line": 28}, "_AsyncRelationshipOperationsMixin._create_many_to_many_relationship": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 8, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 8, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [94, 96, 98, 99, 100, 102, 105, 107], "excluded_lines": [], "start_line": 74}, "_AsyncRelationshipOperationsMixin._delete_relationship": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 4, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [123, 124, 125, 126], "excluded_lines": [], "start_line": 114}, "_AsyncRelationshipOperationsMixin._get_relationship": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 6, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 6, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [140, 141, 142, 143, 144, 145], "excluded_lines": [], "start_line": 128}, "_AsyncRelationshipOperationsMixin._list_relationships": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 8, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 8, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [171, 172, 173, 174, 175, 176, 177, 178], "excluded_lines": [], "start_line": 147}, "_AsyncRelationshipOperationsMixin._list_table_relationships": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 20, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 20, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [210, 211, 213, 214, 215, 220, 228, 229, 230, 231, 232, 233, 234, 236, 237, 238, 240, 241, 242, 244], "excluded_lines": [], "start_line": 180}, "_AsyncRelationshipOperationsMixin._extract_id_from_header": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 4, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [260, 261, 262, 263], "excluded_lines": [], "start_line": 250}, "": {"executed_lines": [10, 12, 14, 15, 18, 28, 74, 114, 128, 147, 180, 250], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_AsyncRelationshipOperationsMixin": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 59, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 59, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [51, 54, 55, 57, 58, 59, 61, 64, 66, 94, 96, 98, 99, 100, 102, 105, 107, 123, 124, 125, 126, 140, 141, 142, 143, 144, 145, 171, 172, 173, 174, 175, 176, 177, 178, 210, 211, 213, 214, 215, 220, 228, 229, 230, 231, 232, 233, 234, 236, 237, 238, 240, 241, 242, 244, 260, 261, 262, 263], "excluded_lines": [], "start_line": 18}, "": {"executed_lines": [10, 12, 14, 15, 18, 28, 74, 114, 128, 147, 180, 250], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\data\\_async_upload.py": {"executed_lines": [6, 8, 11, 14, 80, 117], "summary": {"covered_lines": 6, "num_statements": 87, "percent_covered": 6.896551724137931, "percent_covered_display": "7", "missing_lines": 81, "excluded_lines": 0, "percent_statements_covered": 6.896551724137931, "percent_statements_covered_display": "7"}, "missing_lines": [43, 46, 49, 50, 51, 52, 53, 54, 56, 59, 61, 63, 64, 65, 66, 67, 70, 72, 73, 76, 77, 78, 90, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 109, 110, 112, 114, 115, 147, 148, 150, 151, 152, 153, 154, 155, 156, 157, 158, 161, 162, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 193, 194, 195], "excluded_lines": [], "functions": {"_AsyncFileUploadMixin._upload_file": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 22, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 22, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [43, 46, 49, 50, 51, 52, 53, 54, 56, 59, 61, 63, 64, 65, 66, 67, 70, 72, 73, 76, 77, 78], "excluded_lines": [], "start_line": 14}, "_AsyncFileUploadMixin._upload_file_small": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 20, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 20, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [90, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 109, 110, 112, 114, 115], "excluded_lines": [], "start_line": 80}, "_AsyncFileUploadMixin._upload_file_chunk": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 39, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 39, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [147, 148, 150, 151, 152, 153, 154, 155, 156, 157, 158, 161, 162, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 193, 194, 195], "excluded_lines": [], "start_line": 117}, "": {"executed_lines": [6, 8, 11, 14, 80, 117], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_AsyncFileUploadMixin": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 81, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 81, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [43, 46, 49, 50, 51, 52, 53, 54, 56, 59, 61, 63, 64, 65, 66, 67, 70, 72, 73, 76, 77, 78, 90, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 109, 110, 112, 114, 115, 147, 148, 150, 151, 152, 153, 154, 155, 156, 157, 158, 161, 162, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 193, 194, 195], "excluded_lines": [], "start_line": 11}, "": {"executed_lines": [6, 8, 11, 14, 80, 117], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\operations\\__init__.py": {"executed_lines": [11, 13], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [11, 13], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [11, 13], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\operations\\async_batch.py": {"executed_lines": [6, 8, 10, 12, 13, 14, 34, 35, 36, 37, 42, 47, 64, 75, 76, 78, 101, 103, 116, 118, 128, 131, 149, 150, 151, 153, 154, 156, 157, 165, 177, 178, 180, 197, 199, 219, 221, 243, 245, 265, 267, 286, 287, 288, 289, 290, 291, 292, 293, 295, 296, 299, 315, 316, 318, 341, 351, 360, 362, 369, 371, 385, 387, 398, 400, 412, 414, 431, 433, 447, 449, 456, 458, 465, 467, 503, 523, 534, 535, 537, 554, 555, 556, 564, 574, 575, 577, 591, 592, 593, 594, 596, 598, 599, 600, 601, 605, 607, 629, 630, 631, 632, 633, 634, 636, 637, 638, 639, 643, 645, 646, 647, 651, 653, 654, 655, 656, 657, 658, 660, 662, 681, 682, 683, 684, 685, 686, 687, 688, 689, 690, 698, 727, 728, 729, 730, 731, 732, 733, 734, 736, 751, 752, 753, 755, 773, 774, 777, 793, 794, 796, 802], "summary": {"covered_lines": 152, "num_statements": 152, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [44, 45], "functions": {"AsyncChangeSetRecordOperations.__init__": {"executed_lines": [76], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 75}, "AsyncChangeSetRecordOperations.create": {"executed_lines": [101], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 78}, "AsyncChangeSetRecordOperations.update": {"executed_lines": [116], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 103}, "AsyncChangeSetRecordOperations.delete": {"executed_lines": [128], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 118}, "AsyncChangeSet.__init__": {"executed_lines": [150, 151], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 149}, "AsyncChangeSet.__aenter__": {"executed_lines": [154], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 153}, "AsyncChangeSet.__aexit__": {"executed_lines": [157], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 156}, "AsyncBatchRecordOperations.__init__": {"executed_lines": [178], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 177}, "AsyncBatchRecordOperations.create": {"executed_lines": [197], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 180}, "AsyncBatchRecordOperations.update": {"executed_lines": [219], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 199}, "AsyncBatchRecordOperations.delete": {"executed_lines": [243], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 221}, "AsyncBatchRecordOperations.get": {"executed_lines": [265], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 245}, "AsyncBatchRecordOperations.upsert": {"executed_lines": [286, 287, 288, 289, 290, 291, 292, 293, 295, 296], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 267}, "AsyncBatchTableOperations.__init__": {"executed_lines": [316], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 315}, "AsyncBatchTableOperations.create": {"executed_lines": [341], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 318}, "AsyncBatchTableOperations.delete": {"executed_lines": [360], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 351}, "AsyncBatchTableOperations.get": {"executed_lines": [369], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 362}, "AsyncBatchTableOperations.list": {"executed_lines": [385], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 371}, "AsyncBatchTableOperations.add_columns": {"executed_lines": [398], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 387}, "AsyncBatchTableOperations.remove_columns": {"executed_lines": [412], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 400}, "AsyncBatchTableOperations.create_one_to_many_relationship": {"executed_lines": [431], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 414}, "AsyncBatchTableOperations.create_many_to_many_relationship": {"executed_lines": [447], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 433}, "AsyncBatchTableOperations.delete_relationship": {"executed_lines": [456], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 449}, "AsyncBatchTableOperations.get_relationship": {"executed_lines": [465], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 458}, "AsyncBatchTableOperations.create_lookup_field": {"executed_lines": [503], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 467}, "AsyncBatchQueryOperations.__init__": {"executed_lines": [535], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 534}, "AsyncBatchQueryOperations.sql": {"executed_lines": [554, 555, 556], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 537}, "AsyncBatchDataFrameOperations.__init__": {"executed_lines": [575], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 574}, "AsyncBatchDataFrameOperations.create": {"executed_lines": [591, 592, 593, 594, 596, 598, 599, 600, 601, 605], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 577}, "AsyncBatchDataFrameOperations.update": {"executed_lines": [629, 630, 631, 632, 633, 634, 636, 637, 638, 639, 643, 645, 646, 647, 651, 653, 654, 655, 656, 657, 658, 660], "summary": {"covered_lines": 22, "num_statements": 22, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 607}, "AsyncBatchDataFrameOperations.delete": {"executed_lines": [681, 682, 683, 684, 685, 686, 687, 688, 689, 690], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 662}, "AsyncBatchRequest.__init__": {"executed_lines": [728, 729, 730, 731, 732, 733, 734], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 727}, "AsyncBatchRequest.changeset": {"executed_lines": [751, 752, 753], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 736}, "AsyncBatchRequest.execute": {"executed_lines": [773, 774], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 755}, "AsyncBatchOperations.__init__": {"executed_lines": [794], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 793}, "AsyncBatchOperations.new": {"executed_lines": [802], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 796}, "": {"executed_lines": [6, 8, 10, 12, 13, 14, 34, 35, 36, 37, 42, 47, 64, 75, 78, 103, 118, 131, 149, 153, 156, 165, 177, 180, 199, 221, 245, 267, 299, 315, 318, 351, 362, 371, 387, 400, 414, 433, 449, 458, 467, 523, 534, 537, 564, 574, 577, 607, 662, 698, 727, 736, 755, 777, 793, 796], "summary": {"covered_lines": 56, "num_statements": 56, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [44, 45], "start_line": 1}}, "classes": {"AsyncChangeSetRecordOperations": {"executed_lines": [76, 101, 116, 128], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 64}, "AsyncChangeSet": {"executed_lines": [150, 151, 154, 157], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 131}, "AsyncBatchRecordOperations": {"executed_lines": [178, 197, 219, 243, 265, 286, 287, 288, 289, 290, 291, 292, 293, 295, 296], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 165}, "AsyncBatchTableOperations": {"executed_lines": [316, 341, 360, 369, 385, 398, 412, 431, 447, 456, 465, 503], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 299}, "AsyncBatchQueryOperations": {"executed_lines": [535, 554, 555, 556], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 523}, "AsyncBatchDataFrameOperations": {"executed_lines": [575, 591, 592, 593, 594, 596, 598, 599, 600, 601, 605, 629, 630, 631, 632, 633, 634, 636, 637, 638, 639, 643, 645, 646, 647, 651, 653, 654, 655, 656, 657, 658, 660, 681, 682, 683, 684, 685, 686, 687, 688, 689, 690], "summary": {"covered_lines": 43, "num_statements": 43, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 564}, "AsyncBatchRequest": {"executed_lines": [728, 729, 730, 731, 732, 733, 734, 751, 752, 753, 773, 774], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 698}, "AsyncBatchOperations": {"executed_lines": [794, 802], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 777}, "": {"executed_lines": [6, 8, 10, 12, 13, 14, 34, 35, 36, 37, 42, 47, 64, 75, 78, 103, 118, 131, 149, 153, 156, 165, 177, 180, 199, 221, 245, 267, 299, 315, 318, 351, 362, 371, 387, 400, 414, 433, 449, 458, 467, 523, 534, 537, 564, 574, 577, 607, 662, 698, 727, 736, 755, 777, 793, 796], "summary": {"covered_lines": 56, "num_statements": 56, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [44, 45], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\operations\\async_dataframe.py": {"executed_lines": [6, 8, 10, 12, 18, 21, 51, 52, 56, 82, 83, 84, 85, 89, 151, 152, 153, 154, 155, 156, 160, 165, 167, 168, 179, 181, 182, 183, 187, 217, 218, 220, 221, 223, 226, 227, 228, 233, 235, 236, 238, 242, 280, 281, 282, 283, 284, 285, 287, 288, 289, 290, 294, 296, 297, 298, 301, 304, 305, 306, 307, 308, 310, 311, 313, 317, 350, 351, 353, 354, 355, 357, 358, 359, 363, 365, 366, 367, 368], "summary": {"covered_lines": 79, "num_statements": 79, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [14, 15], "functions": {"AsyncDataFrameOperations.__init__": {"executed_lines": [52], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 51}, "AsyncDataFrameOperations.sql": {"executed_lines": [82, 83, 84, 85], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 56}, "AsyncDataFrameOperations.get": {"executed_lines": [151, 152, 153, 154, 155, 156, 160, 165, 167, 168, 179, 181, 182, 183], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 89}, "AsyncDataFrameOperations.create": {"executed_lines": [217, 218, 220, 221, 223, 226, 227, 228, 233, 235, 236, 238], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 187}, "AsyncDataFrameOperations.update": {"executed_lines": [280, 281, 282, 283, 284, 285, 287, 288, 289, 290, 294, 296, 297, 298, 301, 304, 305, 306, 307, 308, 310, 311, 313], "summary": {"covered_lines": 23, "num_statements": 23, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 242}, "AsyncDataFrameOperations.delete": {"executed_lines": [350, 351, 353, 354, 355, 357, 358, 359, 363, 365, 366, 367, 368], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 317}, "": {"executed_lines": [6, 8, 10, 12, 18, 21, 51, 56, 89, 187, 242, 317], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [14, 15], "start_line": 1}}, "classes": {"AsyncDataFrameOperations": {"executed_lines": [52, 82, 83, 84, 85, 151, 152, 153, 154, 155, 156, 160, 165, 167, 168, 179, 181, 182, 183, 217, 218, 220, 221, 223, 226, 227, 228, 233, 235, 236, 238, 280, 281, 282, 283, 284, 285, 287, 288, 289, 290, 294, 296, 297, 298, 301, 304, 305, 306, 307, 308, 310, 311, 313, 350, 351, 353, 354, 355, 357, 358, 359, 363, 365, 366, 367, 368], "summary": {"covered_lines": 67, "num_statements": 67, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 21}, "": {"executed_lines": [6, 8, 10, 12, 18, 21, 51, 56, 89, 187, 242, 317], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [14, 15], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\operations\\async_files.py": {"executed_lines": [6, 8, 14, 17, 35, 36, 40, 104, 105], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [10, 11], "functions": {"AsyncFileOperations.__init__": {"executed_lines": [36], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 35}, "AsyncFileOperations.upload": {"executed_lines": [104, 105], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 40}, "": {"executed_lines": [6, 8, 14, 17, 35, 40], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [10, 11], "start_line": 1}}, "classes": {"AsyncFileOperations": {"executed_lines": [36, 104, 105], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 17}, "": {"executed_lines": [6, 8, 14, 17, 35, 40], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [10, 11], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\operations\\async_query.py": {"executed_lines": [6, 8, 10, 11, 17, 20, 39, 40, 44, 93, 94, 95, 99, 129, 138, 151, 152, 153, 154, 156, 157, 160, 161, 163, 164, 165, 166, 167, 169, 178, 179, 183, 208, 209, 213, 246, 247, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 267, 268, 269, 270, 273, 274, 276, 286, 287, 291, 327, 328, 329, 330, 331, 336, 337, 338, 339, 347, 372, 373, 377, 407, 408, 410, 411, 412, 413, 415, 416, 417, 418, 419, 423, 424, 425, 426, 427, 428, 430, 440, 441, 445, 477, 478, 479, 480, 481, 486, 490, 526, 527, 528, 529, 530, 535, 536, 537, 538], "summary": {"covered_lines": 108, "num_statements": 115, "percent_covered": 93.91304347826087, "percent_covered_display": "94", "missing_lines": 7, "excluded_lines": 2, "percent_statements_covered": 93.91304347826087, "percent_statements_covered_display": "94"}, "missing_lines": [155, 168, 260, 271, 272, 414, 420], "excluded_lines": [13, 14], "functions": {"AsyncQueryOperations.__init__": {"executed_lines": [40], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 39}, "AsyncQueryOperations.sql": {"executed_lines": [93, 94, 95], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 44}, "AsyncQueryOperations.sql_columns": {"executed_lines": [129, 138, 151, 152, 153, 154, 156, 157, 160, 161, 163, 164, 165, 166, 167, 169, 178, 179], "summary": {"covered_lines": 18, "num_statements": 20, "percent_covered": 90.0, "percent_covered_display": "90", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 90.0, "percent_statements_covered_display": "90"}, "missing_lines": [155, 168], "excluded_lines": [], "start_line": 99}, "AsyncQueryOperations.sql_select": {"executed_lines": [208, 209], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 183}, "AsyncQueryOperations.sql_joins": {"executed_lines": [246, 247, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 267, 268, 269, 270, 273, 274, 276, 286, 287], "summary": {"covered_lines": 22, "num_statements": 25, "percent_covered": 88.0, "percent_covered_display": "88", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 88.0, "percent_statements_covered_display": "88"}, "missing_lines": [260, 271, 272], "excluded_lines": [], "start_line": 213}, "AsyncQueryOperations.sql_join": {"executed_lines": [327, 328, 329, 330, 331, 336, 337, 338, 339], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 291}, "AsyncQueryOperations.odata_select": {"executed_lines": [372, 373], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 347}, "AsyncQueryOperations.odata_expands": {"executed_lines": [407, 408, 410, 411, 412, 413, 415, 416, 417, 418, 419, 423, 424, 425, 426, 427, 428, 430, 440, 441], "summary": {"covered_lines": 20, "num_statements": 22, "percent_covered": 90.9090909090909, "percent_covered_display": "91", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 90.9090909090909, "percent_statements_covered_display": "91"}, "missing_lines": [414, 420], "excluded_lines": [], "start_line": 377}, "AsyncQueryOperations.odata_expand": {"executed_lines": [477, 478, 479, 480, 481, 486], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 445}, "AsyncQueryOperations.odata_bind": {"executed_lines": [526, 527, 528, 529, 530, 535, 536, 537, 538], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 490}, "": {"executed_lines": [6, 8, 10, 11, 17, 20, 39, 44, 99, 183, 213, 291, 347, 377, 445, 490], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [13, 14], "start_line": 1}}, "classes": {"AsyncQueryOperations": {"executed_lines": [40, 93, 94, 95, 129, 138, 151, 152, 153, 154, 156, 157, 160, 161, 163, 164, 165, 166, 167, 169, 178, 179, 208, 209, 246, 247, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 267, 268, 269, 270, 273, 274, 276, 286, 287, 327, 328, 329, 330, 331, 336, 337, 338, 339, 372, 373, 407, 408, 410, 411, 412, 413, 415, 416, 417, 418, 419, 423, 424, 425, 426, 427, 428, 430, 440, 441, 477, 478, 479, 480, 481, 486, 526, 527, 528, 529, 530, 535, 536, 537, 538], "summary": {"covered_lines": 92, "num_statements": 99, "percent_covered": 92.92929292929293, "percent_covered_display": "93", "missing_lines": 7, "excluded_lines": 0, "percent_statements_covered": 92.92929292929293, "percent_statements_covered_display": "93"}, "missing_lines": [155, 168, 260, 271, 272, 414, 420], "excluded_lines": [], "start_line": 20}, "": {"executed_lines": [6, 8, 10, 11, 17, 20, 39, 44, 99, 183, 213, 291, 347, 377, 445, 490], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [13, 14], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\operations\\async_records.py": {"executed_lines": [6, 8, 10, 11, 17, 20, 46, 47, 57, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 110, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 174, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 228, 229, 262, 263, 331, 428, 429, 430, 431, 440, 445, 446, 447, 449, 450, 451, 462, 464, 468, 516, 517, 518, 519, 520, 521, 522, 523, 525, 526, 527, 528, 529, 530, 532, 533, 534, 535], "summary": {"covered_lines": 86, "num_statements": 86, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 18, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [13, 14, 51, 52, 53, 54, 55, 56, 168, 169, 170, 171, 172, 173, 260, 261, 329, 330], "functions": {"AsyncRecordOperations.__init__": {"executed_lines": [47], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 46}, "AsyncRecordOperations.create": {"executed_lines": [94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 57}, "AsyncRecordOperations.update": {"executed_lines": [155, 156, 157, 158, 159, 160, 161, 162, 163, 164], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 110}, "AsyncRecordOperations.delete": {"executed_lines": [210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 174}, "AsyncRecordOperations.get": {"executed_lines": [428, 429, 430, 431, 440, 445, 446, 447, 449, 464], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 331}, "AsyncRecordOperations.get._paged": {"executed_lines": [450, 451, 462], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 449}, "AsyncRecordOperations.upsert": {"executed_lines": [516, 517, 518, 519, 520, 521, 522, 523, 525, 526, 527, 528, 529, 530, 532, 533, 534, 535], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 468}, "": {"executed_lines": [6, 8, 10, 11, 17, 20, 46, 57, 110, 174, 228, 229, 262, 263, 331, 468], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 12, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [13, 14, 51, 53, 54, 56, 168, 170, 171, 173, 261, 330], "start_line": 1}}, "classes": {"AsyncRecordOperations": {"executed_lines": [47, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 428, 429, 430, 431, 440, 445, 446, 447, 449, 450, 451, 462, 464, 516, 517, 518, 519, 520, 521, 522, 523, 525, 526, 527, 528, 529, 530, 532, 533, 534, 535], "summary": {"covered_lines": 70, "num_statements": 70, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 6, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [52, 55, 169, 172, 260, 329], "start_line": 20}, "": {"executed_lines": [6, 8, 10, 11, 17, 20, 46, 57, 110, 174, 228, 229, 262, 263, 331, 468], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 12, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [13, 14, 51, 53, 54, 56, 168, 170, 171, 173, 261, 330], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\aio\\operations\\async_tables.py": {"executed_lines": [6, 8, 10, 16, 17, 18, 19, 25, 28, 64, 65, 69, 134, 135, 142, 146, 163, 164, 168, 188, 189, 190, 191, 192, 196, 244, 245, 249, 277, 278, 282, 310, 311, 315, 373, 374, 379, 389, 429, 430, 434, 443, 462, 463, 467, 486, 487, 488, 489, 490, 494, 559, 560, 571, 575, 626, 627, 628, 629, 638, 660, 661, 662, 666, 690, 691, 695, 735, 736, 740, 769, 770, 774, 814, 815], "summary": {"covered_lines": 75, "num_statements": 75, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [21, 22], "functions": {"AsyncTableOperations.__init__": {"executed_lines": [65], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 64}, "AsyncTableOperations.create": {"executed_lines": [134, 135, 142], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 69}, "AsyncTableOperations.delete": {"executed_lines": [163, 164], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 146}, "AsyncTableOperations.get": {"executed_lines": [188, 189, 190, 191, 192], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 168}, "AsyncTableOperations.list": {"executed_lines": [244, 245], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 196}, "AsyncTableOperations.add_columns": {"executed_lines": [277, 278], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 249}, "AsyncTableOperations.remove_columns": {"executed_lines": [310, 311], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 282}, "AsyncTableOperations.create_one_to_many_relationship": {"executed_lines": [373, 374, 379], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 315}, "AsyncTableOperations.create_many_to_many_relationship": {"executed_lines": [429, 430, 434], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 389}, "AsyncTableOperations.delete_relationship": {"executed_lines": [462, 463], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 443}, "AsyncTableOperations.get_relationship": {"executed_lines": [486, 487, 488, 489, 490], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 467}, "AsyncTableOperations.create_lookup_field": {"executed_lines": [559, 560, 571], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 494}, "AsyncTableOperations.create_alternate_key": {"executed_lines": [626, 627, 628, 629], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 575}, "AsyncTableOperations.get_alternate_keys": {"executed_lines": [660, 661, 662], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 638}, "AsyncTableOperations.delete_alternate_key": {"executed_lines": [690, 691], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 666}, "AsyncTableOperations.list_columns": {"executed_lines": [735, 736], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 695}, "AsyncTableOperations.list_relationships": {"executed_lines": [769, 770], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 740}, "AsyncTableOperations.list_table_relationships": {"executed_lines": [814, 815], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 774}, "": {"executed_lines": [6, 8, 10, 16, 17, 18, 19, 25, 28, 64, 69, 146, 168, 196, 249, 282, 315, 389, 443, 467, 494, 575, 638, 666, 695, 740, 774], "summary": {"covered_lines": 27, "num_statements": 27, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [21, 22], "start_line": 1}}, "classes": {"AsyncTableOperations": {"executed_lines": [65, 134, 135, 142, 163, 164, 188, 189, 190, 191, 192, 244, 245, 277, 278, 310, 311, 373, 374, 379, 429, 430, 434, 462, 463, 486, 487, 488, 489, 490, 559, 560, 571, 626, 627, 628, 629, 660, 661, 662, 690, 691, 735, 736, 769, 770, 814, 815], "summary": {"covered_lines": 48, "num_statements": 48, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 28}, "": {"executed_lines": [6, 8, 10, 16, 17, 18, 19, 25, 28, 64, 69, 146, 168, 196, 249, 282, 315, 389, 443, 467, 494, 575, 638, 666, 695, 740, 774], "summary": {"covered_lines": 27, "num_statements": 27, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [21, 22], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\claude_skill\\__init__.py": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\client.py": {"executed_lines": [4, 6, 7, 8, 10, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 25, 93, 99, 100, 101, 102, 103, 104, 105, 106, 109, 110, 111, 112, 113, 114, 116, 126, 127, 133, 135, 136, 138, 139, 140, 141, 145, 157, 158, 159, 160, 162, 168, 170, 187, 188, 189, 190, 191, 192, 193, 194, 195, 197, 199, 200, 203, 238, 244, 245, 246, 248, 296, 301, 303, 338, 343, 345, 428, 433, 434, 436, 447, 483, 488, 491, 514, 519, 521, 592, 597, 604, 625, 630, 632, 649, 654, 656, 688, 693, 695, 721, 726, 729, 761, 766, 777, 798, 799, 802], "summary": {"covered_lines": 105, "num_statements": 105, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"DataverseClient.__init__": {"executed_lines": [99, 100, 101, 102, 103, 104, 105, 106, 109, 110, 111, 112, 113, 114], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 93}, "DataverseClient._get_odata": {"executed_lines": [126, 127, 133], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 116}, "DataverseClient._scoped_odata": {"executed_lines": [138, 139, 140, 141], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 136}, "DataverseClient.__enter__": {"executed_lines": [157, 158, 159, 160], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 145}, "DataverseClient.__exit__": {"executed_lines": [168], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 162}, "DataverseClient.close": {"executed_lines": [187, 188, 189, 190, 191, 192, 193, 194, 195], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 170}, "DataverseClient._check_closed": {"executed_lines": [199, 200], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 197}, "DataverseClient.create": {"executed_lines": [238, 244, 245, 246], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 203}, "DataverseClient.update": {"executed_lines": [296, 301], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 248}, "DataverseClient.delete": {"executed_lines": [338, 343], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 303}, "DataverseClient.get": {"executed_lines": [428, 433, 434, 436], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 345}, "DataverseClient.query_sql": {"executed_lines": [483, 488], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 447}, "DataverseClient.get_table_info": {"executed_lines": [514, 519], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 491}, "DataverseClient.create_table": {"executed_lines": [592, 597], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 521}, "DataverseClient.delete_table": {"executed_lines": [625, 630], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 604}, "DataverseClient.list_tables": {"executed_lines": [649, 654], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 632}, "DataverseClient.create_columns": {"executed_lines": [688, 693], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 656}, "DataverseClient.delete_columns": {"executed_lines": [721, 726], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 695}, "DataverseClient.upload_file": {"executed_lines": [761, 766], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 729}, "DataverseClient.flush_cache": {"executed_lines": [798, 799], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 777}, "": {"executed_lines": [4, 6, 7, 8, 10, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 25, 93, 116, 135, 136, 145, 162, 170, 197, 203, 248, 303, 345, 447, 491, 521, 604, 632, 656, 695, 729, 777, 802], "summary": {"covered_lines": 38, "num_statements": 38, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"DataverseClient": {"executed_lines": [99, 100, 101, 102, 103, 104, 105, 106, 109, 110, 111, 112, 113, 114, 126, 127, 133, 138, 139, 140, 141, 157, 158, 159, 160, 168, 187, 188, 189, 190, 191, 192, 193, 194, 195, 199, 200, 238, 244, 245, 246, 296, 301, 338, 343, 428, 433, 434, 436, 483, 488, 514, 519, 592, 597, 625, 630, 649, 654, 688, 693, 721, 726, 761, 766, 798, 799], "summary": {"covered_lines": 67, "num_statements": 67, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 25}, "": {"executed_lines": [4, 6, 7, 8, 10, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 25, 93, 116, 135, 136, 145, 162, 170, 197, 203, 248, 303, 345, 447, 491, 521, 604, 632, 656, 695, 729, 777, 802], "summary": {"covered_lines": 38, "num_statements": 38, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\common\\__init__.py": {"executed_lines": [10], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [10], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [10], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\common\\constants.py": {"executed_lines": [12, 13, 14, 15, 16, 21, 22, 24, 25, 27, 28, 30, 31], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [12, 13, 14, 15, 16, 21, 22, 24, 25, 27, 28, 30, 31], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [12, 13, 14, 15, 16, 21, 22, 24, 25, 27, 28, 30, 31], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\core\\__init__.py": {"executed_lines": [11], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [11], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [11], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\core\\_auth.py": {"executed_lines": [12, 14, 16, 19, 20, 30, 31, 34, 43, 44, 45, 46, 48, 58, 59], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"_AuthManager.__init__": {"executed_lines": [44, 45, 46], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 43}, "_AuthManager._acquire_token": {"executed_lines": [58, 59], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 48}, "": {"executed_lines": [12, 14, 16, 19, 20, 30, 31, 34, 43, 48], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_TokenPair": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 20}, "_AuthManager": {"executed_lines": [44, 45, 46, 58, 59], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 34}, "": {"executed_lines": [12, 14, 16, 19, 20, 30, 31, 34, 43, 48], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\core\\_error_codes.py": {"executed_lines": [12, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 28, 44, 45, 46, 47, 48, 49, 50, 51, 54, 57, 58, 59, 60, 61, 62, 63, 66, 81, 84, 93, 96, 108], "summary": {"covered_lines": 36, "num_statements": 36, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"_http_subcode": {"executed_lines": [93], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 84}, "_is_transient_status": {"executed_lines": [108], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 96}, "": {"executed_lines": [12, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 28, 44, 45, 46, 47, 48, 49, 50, 51, 54, 57, 58, 59, 60, 61, 62, 63, 66, 81, 84, 96], "summary": {"covered_lines": 34, "num_statements": 34, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [12, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 28, 44, 45, 46, 47, 48, 49, 50, 51, 54, 57, 58, 59, 60, 61, 62, 63, 66, 81, 84, 93, 96, 108], "summary": {"covered_lines": 36, "num_statements": 36, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\core\\_http.py": {"executed_lines": [12, 14, 15, 17, 23, 45, 53, 54, 55, 56, 57, 59, 77, 78, 79, 81, 82, 86, 87, 88, 89, 92, 93, 101, 102, 103, 104, 105, 106, 108, 111, 112, 120, 121, 122, 123, 130, 131, 132, 133, 134, 136, 141, 142, 143], "summary": {"covered_lines": 45, "num_statements": 46, "percent_covered": 97.82608695652173, "percent_covered_display": "98", "missing_lines": 1, "excluded_lines": 2, "percent_statements_covered": 97.82608695652173, "percent_statements_covered_display": "98"}, "missing_lines": [90], "excluded_lines": [19, 20], "functions": {"_HttpClient.__init__": {"executed_lines": [53, 54, 55, 56, 57], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 45}, "_HttpClient._request": {"executed_lines": [77, 78, 79, 81, 82, 86, 87, 88, 89, 92, 93, 101, 102, 103, 104, 105, 106, 108, 111, 112, 120, 121, 122, 123, 130, 131, 132, 133, 134], "summary": {"covered_lines": 29, "num_statements": 30, "percent_covered": 96.66666666666667, "percent_covered_display": "97", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 96.66666666666667, "percent_statements_covered_display": "97"}, "missing_lines": [90], "excluded_lines": [], "start_line": 59}, "_HttpClient.close": {"executed_lines": [141, 142, 143], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 136}, "": {"executed_lines": [12, 14, 15, 17, 23, 45, 59, 136], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [19, 20], "start_line": 1}}, "classes": {"_HttpClient": {"executed_lines": [53, 54, 55, 56, 57, 77, 78, 79, 81, 82, 86, 87, 88, 89, 92, 93, 101, 102, 103, 104, 105, 106, 108, 111, 112, 120, 121, 122, 123, 130, 131, 132, 133, 134, 141, 142, 143], "summary": {"covered_lines": 37, "num_statements": 38, "percent_covered": 97.36842105263158, "percent_covered_display": "97", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 97.36842105263158, "percent_statements_covered_display": "97"}, "missing_lines": [90], "excluded_lines": [], "start_line": 23}, "": {"executed_lines": [12, 14, 15, 17, 23, 45, 59, 136], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [19, 20], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\core\\_http_logger.py": {"executed_lines": [8, 10, 11, 12, 13, 14, 15, 16, 18, 21, 24, 25, 26, 29, 33, 34, 35, 38, 39, 40, 41, 43, 49, 53, 54, 57, 62, 63, 75, 83, 84, 85, 86, 87, 88, 89, 91, 101, 102, 103, 104, 105, 106, 107, 108, 111, 112, 113, 114, 117, 118, 119, 121, 130, 131, 133, 134, 136, 138, 140, 141, 142, 144, 145, 147, 148, 149, 150, 151, 152, 153, 154, 158, 160, 161, 162, 163, 164, 167, 168, 169], "summary": {"covered_lines": 81, "num_statements": 85, "percent_covered": 95.29411764705883, "percent_covered_display": "95", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 95.29411764705883, "percent_statements_covered_display": "95"}, "missing_lines": [115, 116, 155, 156], "excluded_lines": [], "functions": {"_HttpLogger.__init__": {"executed_lines": [25, 26, 29, 33, 34, 35, 38, 39, 40, 41, 43, 49, 53, 54, 57, 62, 63], "summary": {"covered_lines": 17, "num_statements": 17, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 24}, "_HttpLogger.log_request": {"executed_lines": [83, 84, 85, 86, 87, 88, 89], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 75}, "_HttpLogger.log_response": {"executed_lines": [101, 102, 103, 104, 105, 106, 107, 108, 111, 112, 113, 114, 117, 118, 119], "summary": {"covered_lines": 15, "num_statements": 17, "percent_covered": 88.23529411764706, "percent_covered_display": "88", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 88.23529411764706, "percent_statements_covered_display": "88"}, "missing_lines": [115, 116], "excluded_lines": [], "start_line": 91}, "_HttpLogger.log_error": {"executed_lines": [130, 131], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 121}, "_HttpLogger.body_logging_enabled": {"executed_lines": [136], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 134}, "_HttpLogger.close": {"executed_lines": [140, 141, 142], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 138}, "_HttpLogger._redact_headers": {"executed_lines": [145], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 144}, "_HttpLogger._truncate_body": {"executed_lines": [148, 149, 150, 151, 152, 153, 154, 158, 160, 161, 162, 163, 164, 167, 168, 169], "summary": {"covered_lines": 16, "num_statements": 18, "percent_covered": 88.88888888888889, "percent_covered_display": "89", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 88.88888888888889, "percent_statements_covered_display": "89"}, "missing_lines": [155, 156], "excluded_lines": [], "start_line": 147}, "": {"executed_lines": [8, 10, 11, 12, 13, 14, 15, 16, 18, 21, 24, 75, 91, 121, 133, 134, 138, 144, 147], "summary": {"covered_lines": 19, "num_statements": 19, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_HttpLogger": {"executed_lines": [25, 26, 29, 33, 34, 35, 38, 39, 40, 41, 43, 49, 53, 54, 57, 62, 63, 83, 84, 85, 86, 87, 88, 89, 101, 102, 103, 104, 105, 106, 107, 108, 111, 112, 113, 114, 117, 118, 119, 130, 131, 136, 140, 141, 142, 145, 148, 149, 150, 151, 152, 153, 154, 158, 160, 161, 162, 163, 164, 167, 168, 169], "summary": {"covered_lines": 62, "num_statements": 66, "percent_covered": 93.93939393939394, "percent_covered_display": "94", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 93.93939393939394, "percent_statements_covered_display": "94"}, "missing_lines": [115, 116, 155, 156], "excluded_lines": [], "start_line": 21}, "": {"executed_lines": [8, 10, 11, 12, 13, 14, 15, 16, 18, 21, 24, 75, 91, 121, 133, 134, 138, 144, 147], "summary": {"covered_lines": 19, "num_statements": 19, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\core\\config.py": {"executed_lines": [12, 14, 15, 21, 22, 40, 43, 44, 45, 47, 49, 50, 58], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [17, 18], "functions": {"DataverseConfig.from_env": {"executed_lines": [58], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 50}, "": {"executed_lines": [12, 14, 15, 21, 22, 40, 43, 44, 45, 47, 49, 50], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [17, 18], "start_line": 1}}, "classes": {"DataverseConfig": {"executed_lines": [58], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 22}, "": {"executed_lines": [12, 14, 15, 21, 22, 40, 43, 44, 45, 47, 49, 50], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [17, 18], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\core\\errors.py": {"executed_lines": [15, 16, 17, 20, 40, 50, 51, 52, 53, 54, 55, 56, 57, 58, 60, 67, 82, 94, 95, 98, 110, 111, 114, 126, 127, 130, 160, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 201], "summary": {"covered_lines": 44, "num_statements": 44, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [78, 79], "functions": {"DataverseError.__init__": {"executed_lines": [50, 51, 52, 53, 54, 55, 56, 57, 58], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 40}, "DataverseError.to_dict": {"executed_lines": [67], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 60}, "DataverseError.__repr__": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 1, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [79], "start_line": 78}, "ValidationError.__init__": {"executed_lines": [95], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 94}, "MetadataError.__init__": {"executed_lines": [111], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 110}, "SQLParseError.__init__": {"executed_lines": [127], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 126}, "HttpError.__init__": {"executed_lines": [175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 160}, "": {"executed_lines": [15, 16, 17, 20, 40, 60, 82, 94, 98, 110, 114, 126, 130, 160, 201], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 1, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [78], "start_line": 1}}, "classes": {"DataverseError": {"executed_lines": [50, 51, 52, 53, 54, 55, 56, 57, 58, 67], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 1, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [79], "start_line": 20}, "ValidationError": {"executed_lines": [95], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 82}, "MetadataError": {"executed_lines": [111], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 98}, "SQLParseError": {"executed_lines": [127], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 114}, "HttpError": {"executed_lines": [175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 130}, "": {"executed_lines": [15, 16, 17, 20, 40, 60, 82, 94, 98, 110, 114, 126, 130, 160, 201], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 1, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [78], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\core\\log_config.py": {"executed_lines": [11, 13, 14, 16, 18, 21, 31, 32, 35, 36, 61, 62, 63, 67, 68, 69, 72, 73, 74, 75], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"_default_redacted_headers": {"executed_lines": [32], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 31}, "LogConfig.__post_init__": {"executed_lines": [62, 63], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 61}, "": {"executed_lines": [11, 13, 14, 16, 18, 21, 31, 35, 36, 61, 67, 68, 69, 72, 73, 74, 75], "summary": {"covered_lines": 17, "num_statements": 17, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"LogConfig": {"executed_lines": [62, 63], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 36}, "": {"executed_lines": [11, 13, 14, 16, 18, 21, 31, 32, 35, 36, 61, 67, 68, 69, 72, 73, 74, 75], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\data\\__init__.py": {"executed_lines": [11], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [11], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [11], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\data\\_batch.py": {"executed_lines": [6, 8, 9, 11, 12, 13, 14, 15, 42, 50, 62, 73, 74, 76, 78, 79, 80, 87, 88, 90, 93, 94, 96, 97, 108, 114, 115, 116, 117, 118, 120, 121, 122, 124, 125, 127, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 168, 170, 171, 172, 176, 182, 183, 184, 185, 186, 188, 189, 190, 191, 192, 193, 194, 196, 197, 198, 199, 200, 201, 202, 203, 204, 206, 207, 209, 210, 211, 212, 213, 214, 215, 216, 224, 226, 227, 228, 232, 234, 235, 236, 238, 239, 240, 242, 243, 244, 245, 246, 247, 250, 251, 255, 256, 262, 263], "summary": {"covered_lines": 130, "num_statements": 130, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [39, 40], "functions": {"_BatchClient.execute": {"executed_lines": [73, 74, 76, 78, 79, 80, 87, 88, 90, 93, 94, 96, 97, 108], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 62}, "_BatchClient._resolve_all": {"executed_lines": [115, 116, 117, 118, 120, 121, 122, 124, 125], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 114}, "_BatchClient._resolve_item": {"executed_lines": [129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163], "summary": {"covered_lines": 35, "num_statements": 35, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 127}, "_BatchClient._resolve_one": {"executed_lines": [170, 171, 172, 176], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 168}, "_BatchClient._resolve_record_create": {"executed_lines": [183, 184, 185, 186], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 182}, "_BatchClient._resolve_record_update": {"executed_lines": [189, 190, 191, 192, 193, 194], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 188}, "_BatchClient._resolve_record_delete": {"executed_lines": [197, 198, 199, 200, 201, 202, 203, 204], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 196}, "_BatchClient._resolve_record_get": {"executed_lines": [207], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 206}, "_BatchClient._resolve_record_upsert": {"executed_lines": [210, 211, 212, 213, 214, 215, 216], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 209}, "_BatchClient._require_entity_metadata": {"executed_lines": [226, 227, 228, 232], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 224}, "_BatchClient._resolve_table_delete": {"executed_lines": [235, 236], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 234}, "_BatchClient._resolve_table_add_columns": {"executed_lines": [239, 240], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 238}, "_BatchClient._resolve_table_remove_columns": {"executed_lines": [243, 244, 245, 246, 247, 250, 251, 255, 256], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 242}, "_BatchClient._resolve_query_sql": {"executed_lines": [263], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 262}, "": {"executed_lines": [6, 8, 9, 11, 12, 13, 14, 15, 42, 50, 62, 114, 127, 168, 182, 188, 196, 206, 209, 224, 234, 238, 242, 262], "summary": {"covered_lines": 24, "num_statements": 24, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [39, 40], "start_line": 1}}, "classes": {"_BatchClient": {"executed_lines": [73, 74, 76, 78, 79, 80, 87, 88, 90, 93, 94, 96, 97, 108, 115, 116, 117, 118, 120, 121, 122, 124, 125, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 170, 171, 172, 176, 183, 184, 185, 186, 189, 190, 191, 192, 193, 194, 197, 198, 199, 200, 201, 202, 203, 204, 207, 210, 211, 212, 213, 214, 215, 216, 226, 227, 228, 232, 235, 236, 239, 240, 243, 244, 245, 246, 247, 250, 251, 255, 256, 263], "summary": {"covered_lines": 106, "num_statements": 106, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 50}, "": {"executed_lines": [6, 8, 9, 11, 12, 13, 14, 15, 42, 50, 62, 114, 127, 168, 182, 188, 196, 206, 209, 224, 234, 238, 242, 262], "summary": {"covered_lines": 24, "num_statements": 24, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [39, 40], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\data\\_batch_base.py": {"executed_lines": [9, 11, 12, 13, 14, 15, 17, 18, 19, 20, 25, 26, 27, 28, 33, 35, 36, 47, 48, 49, 50, 51, 54, 55, 56, 57, 58, 59, 62, 63, 64, 65, 66, 67, 70, 71, 72, 73, 74, 77, 78, 79, 80, 86, 87, 88, 89, 90, 91, 92, 95, 96, 97, 100, 101, 102, 105, 106, 107, 108, 111, 112, 113, 114, 117, 118, 119, 120, 123, 124, 125, 126, 127, 130, 131, 132, 133, 136, 137, 138, 141, 142, 143, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 162, 163, 164, 172, 173, 186, 187, 189, 191, 192, 193, 194, 196, 198, 199, 200, 202, 204, 205, 206, 216, 217, 220, 228, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 253, 256, 257, 258, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 283, 284, 285, 287, 288, 289, 290, 291, 292, 293, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 344, 345, 346, 347, 362, 372, 373, 379, 380, 382, 383, 385, 386, 388, 389, 390, 391, 393, 394, 396, 397, 399, 400, 402, 403, 413, 414, 415, 421, 426, 427, 428, 429, 431, 432, 434, 436, 441, 442, 444, 445, 446, 447, 448, 449, 450, 451, 452, 454, 455, 456, 458, 459, 460, 461, 462, 464, 467, 473, 474, 475, 476, 481, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 495, 496, 497, 498], "summary": {"covered_lines": 287, "num_statements": 288, "percent_covered": 99.65277777777777, "percent_covered_display": "99", "missing_lines": 1, "excluded_lines": 2, "percent_statements_covered": 99.65277777777777, "percent_statements_covered_display": "99"}, "missing_lines": [482], "excluded_lines": [30, 31], "functions": {"_ChangeSet.add_create": {"executed_lines": [191, 192, 193, 194], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 189}, "_ChangeSet.add_update": {"executed_lines": [198, 199, 200], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 196}, "_ChangeSet.add_delete": {"executed_lines": [204, 205, 206], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 202}, "_raise_top_level_batch_error": {"executed_lines": [236, 237, 238, 239, 240, 241, 242, 243, 244, 245], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 228}, "_extract_boundary": {"executed_lines": [257, 258], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 256}, "_split_multipart": {"executed_lines": [262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280], "summary": {"covered_lines": 19, "num_statements": 19, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 261}, "_parse_mime_part": {"executed_lines": [284, 285, 287, 288, 289, 290, 291, 292, 293], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 283}, "_parse_http_response_part": {"executed_lines": [297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 344, 345, 346, 347], "summary": {"covered_lines": 50, "num_statements": 50, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 296}, "_BatchBase.__init__": {"executed_lines": [373], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 372}, "_BatchBase._resolve_table_create": {"executed_lines": [380], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 379}, "_BatchBase._resolve_table_get": {"executed_lines": [383], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 382}, "_BatchBase._resolve_table_list": {"executed_lines": [386], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 385}, "_BatchBase._resolve_table_create_one_to_many": {"executed_lines": [389, 390, 391], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 388}, "_BatchBase._resolve_table_create_many_to_many": {"executed_lines": [394], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 393}, "_BatchBase._resolve_table_delete_relationship": {"executed_lines": [397], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 396}, "_BatchBase._resolve_table_get_relationship": {"executed_lines": [400], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 399}, "_BatchBase._resolve_table_create_lookup_field": {"executed_lines": [403, 413, 414, 415], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 402}, "_BatchBase._build_batch_body": {"executed_lines": [426, 427, 428, 429, 431, 432], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 421}, "_BatchBase._serialize_raw_request": {"executed_lines": [436, 441, 442, 444, 445, 446, 447, 448, 449, 450, 451, 452, 454, 455, 456], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 434}, "_BatchBase._serialize_changeset_item": {"executed_lines": [459, 460, 461, 462, 464, 467], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 458}, "_BatchBase._parse_batch_response": {"executed_lines": [474, 475, 476, 481, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 495, 496, 497, 498], "summary": {"covered_lines": 19, "num_statements": 20, "percent_covered": 95.0, "percent_covered_display": "95", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 95.0, "percent_statements_covered_display": "95"}, "missing_lines": [482], "excluded_lines": [], "start_line": 473}, "": {"executed_lines": [9, 11, 12, 13, 14, 15, 17, 18, 19, 20, 25, 26, 27, 28, 33, 35, 36, 47, 48, 49, 50, 51, 54, 55, 56, 57, 58, 59, 62, 63, 64, 65, 66, 67, 70, 71, 72, 73, 74, 77, 78, 79, 80, 86, 87, 88, 89, 90, 91, 92, 95, 96, 97, 100, 101, 102, 105, 106, 107, 108, 111, 112, 113, 114, 117, 118, 119, 120, 123, 124, 125, 126, 127, 130, 131, 132, 133, 136, 137, 138, 141, 142, 143, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 162, 163, 164, 172, 173, 186, 187, 189, 196, 202, 216, 217, 220, 228, 253, 256, 261, 283, 296, 362, 372, 379, 382, 385, 388, 393, 396, 399, 402, 421, 434, 458, 473], "summary": {"covered_lines": 127, "num_statements": 127, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [30, 31], "start_line": 1}}, "classes": {"_RecordCreate": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 48}, "_RecordUpdate": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 55}, "_RecordDelete": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 63}, "_RecordGet": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 71}, "_RecordUpsert": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 78}, "_TableCreate": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 87}, "_TableDelete": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 96}, "_TableGet": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 101}, "_TableList": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 106}, "_TableAddColumns": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 112}, "_TableRemoveColumns": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 118}, "_TableCreateOneToMany": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 124}, "_TableCreateManyToMany": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 131}, "_TableDeleteRelationship": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 137}, "_TableGetRelationship": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 142}, "_TableCreateLookupField": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 147}, "_QuerySql": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 163}, "_ChangeSet": {"executed_lines": [191, 192, 193, 194, 198, 199, 200, 204, 205, 206], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 173}, "_ChangeSetBatchItem": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 217}, "_BatchBase": {"executed_lines": [373, 380, 383, 386, 389, 390, 391, 394, 397, 400, 403, 413, 414, 415, 426, 427, 428, 429, 431, 432, 436, 441, 442, 444, 445, 446, 447, 448, 449, 450, 451, 452, 454, 455, 456, 459, 460, 461, 462, 464, 467, 474, 475, 476, 481, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 495, 496, 497, 498], "summary": {"covered_lines": 60, "num_statements": 61, "percent_covered": 98.36065573770492, "percent_covered_display": "98", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 98.36065573770492, "percent_statements_covered_display": "98"}, "missing_lines": [482], "excluded_lines": [], "start_line": 362}, "": {"executed_lines": [9, 11, 12, 13, 14, 15, 17, 18, 19, 20, 25, 26, 27, 28, 33, 35, 36, 47, 48, 49, 50, 51, 54, 55, 56, 57, 58, 59, 62, 63, 64, 65, 66, 67, 70, 71, 72, 73, 74, 77, 78, 79, 80, 86, 87, 88, 89, 90, 91, 92, 95, 96, 97, 100, 101, 102, 105, 106, 107, 108, 111, 112, 113, 114, 117, 118, 119, 120, 123, 124, 125, 126, 127, 130, 131, 132, 133, 136, 137, 138, 141, 142, 143, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 162, 163, 164, 172, 173, 186, 187, 189, 196, 202, 216, 217, 220, 228, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 253, 256, 257, 258, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 283, 284, 285, 287, 288, 289, 290, 291, 292, 293, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 344, 345, 346, 347, 362, 372, 379, 382, 385, 388, 393, 396, 399, 402, 421, 434, 458, 473], "summary": {"covered_lines": 217, "num_statements": 217, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [30, 31], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\data\\_odata.py": {"executed_lines": [6, 8, 10, 11, 12, 13, 14, 16, 18, 19, 20, 21, 22, 23, 36, 46, 49, 70, 71, 72, 80, 86, 87, 88, 90, 92, 93, 94, 103, 104, 105, 106, 107, 108, 109, 111, 112, 114, 115, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 140, 141, 142, 143, 144, 145, 146, 147, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 179, 185, 186, 187, 188, 189, 190, 193, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 225, 241, 242, 243, 244, 245, 246, 247, 248, 249, 251, 252, 253, 255, 256, 258, 259, 260, 262, 263, 264, 265, 266, 267, 269, 295, 296, 297, 298, 299, 301, 332, 333, 336, 337, 338, 339, 340, 341, 342, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 357, 359, 360, 361, 362, 364, 365, 366, 367, 368, 372, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 409, 424, 425, 426, 427, 431, 432, 433, 434, 435, 436, 437, 438, 440, 452, 454, 472, 473, 474, 475, 477, 488, 490, 503, 505, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 553, 554, 555, 556, 557, 558, 559, 561, 562, 563, 564, 566, 567, 569, 570, 572, 573, 575, 576, 577, 578, 579, 581, 582, 583, 584, 586, 587, 588, 590, 591, 592, 593, 594, 595, 598, 617, 618, 619, 620, 621, 626, 628, 629, 630, 631, 632, 635, 636, 637, 638, 639, 641, 642, 643, 646, 647, 648, 649, 650, 652, 653, 662, 663, 667, 668, 669, 670, 679, 680, 681, 682, 683, 684, 691, 692, 693, 694, 695, 702, 703, 704, 705, 706, 707, 708, 709, 710, 712, 715, 720, 721, 724, 725, 726, 727, 728, 730, 731, 732, 736, 737, 738, 739, 740, 741, 742, 743, 748, 752, 753, 754, 755, 759, 760, 761, 762, 763, 766, 777, 779, 780, 781, 785, 786, 787, 789, 796, 797, 809, 810, 811, 812, 813, 817, 818, 821, 822, 823, 825, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843, 844, 845, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859, 861, 889, 890, 891, 895, 896, 897, 898, 899, 900, 901, 902, 903, 905, 918, 919, 920, 921, 923, 924, 925, 926, 927, 928, 929, 930, 931, 934, 939, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 954, 962, 963, 964, 965, 966, 968, 969, 974, 975, 976, 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, 990, 992, 993, 995, 996, 997, 998, 999, 1000, 1001, 1002, 1003, 1004, 1006, 1008, 1017, 1020, 1024, 1025, 1028, 1031, 1032, 1033, 1035, 1037, 1038, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1049, 1050, 1052, 1061, 1062, 1063, 1064, 1074, 1101, 1103, 1115, 1116, 1117, 1121, 1125, 1152, 1153, 1154, 1159, 1160, 1161, 1165, 1166, 1167, 1168, 1170, 1176, 1190, 1191, 1192, 1197, 1198, 1199, 1200, 1202, 1218, 1219, 1220, 1225, 1226, 1227, 1229, 1259, 1260, 1261, 1266, 1270, 1271, 1273, 1277, 1278, 1279, 1280, 1281, 1282, 1283, 1284, 1286, 1287, 1288, 1289, 1290, 1292, 1293, 1294, 1296, 1303, 1313, 1333, 1334, 1336, 1337, 1338, 1343, 1344, 1345, 1347, 1348, 1349, 1350, 1354, 1355, 1356, 1361, 1362, 1364, 1365, 1367, 1369, 1390, 1391, 1392, 1393, 1395, 1397, 1398, 1399, 1401, 1402, 1403, 1409, 1410, 1411, 1412, 1414, 1415, 1416, 1417, 1422, 1423, 1424, 1426, 1428, 1429, 1430, 1431, 1432, 1434, 1436, 1437, 1439, 1443, 1452, 1453, 1454, 1461, 1468, 1470, 1471, 1472, 1473, 1474, 1475, 1476, 1477, 1478, 1484, 1497, 1498, 1499, 1502, 1503, 1504, 1512, 1524, 1525, 1526, 1527, 1528, 1529, 1530, 1531, 1532, 1538, 1560, 1582, 1590, 1591, 1595, 1596, 1597, 1598, 1599, 1600, 1601, 1604, 1605, 1609, 1610, 1611, 1612, 1613, 1614, 1620, 1631, 1634, 1635, 1636, 1643, 1645, 1646, 1647, 1648, 1679, 1685, 1693, 1694, 1695, 1696, 1697, 1699, 1714, 1715, 1716], "summary": {"covered_lines": 670, "num_statements": 690, "percent_covered": 97.10144927536231, "percent_covered_display": "97", "missing_lines": 20, "excluded_lines": 0, "percent_statements_covered": 97.10144927536231, "percent_statements_covered_display": "97"}, "missing_lines": [991, 994, 1034, 1469, 1500, 1546, 1547, 1548, 1549, 1550, 1551, 1555, 1557, 1558, 1572, 1573, 1574, 1575, 1576, 1632], "excluded_lines": [], "functions": {"_ODataClient.__init__": {"executed_lines": [70, 71, 72], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 49}, "_ODataClient.close": {"executed_lines": [86, 87, 88], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 80}, "_ODataClient._headers": {"executed_lines": [92, 93, 94], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 90}, "_ODataClient._merge_headers": {"executed_lines": [104, 105, 106, 107, 108, 109], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 103}, "_ODataClient._raw_request": {"executed_lines": [112], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 111}, "_ODataClient._request": {"executed_lines": [115, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 140, 141, 142, 143, 144, 145, 146, 147, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161], "summary": {"covered_lines": 35, "num_statements": 35, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 114}, "_ODataClient._execute_raw": {"executed_lines": [185, 186, 187, 188, 189, 190], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 179}, "_ODataClient._create": {"executed_lines": [209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 193}, "_ODataClient._create_multiple": {"executed_lines": [241, 242, 243, 244, 245, 246, 247, 248, 249, 251, 252, 253, 255, 256, 258, 259, 260, 262, 263, 264, 265, 266, 267], "summary": {"covered_lines": 23, "num_statements": 23, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 225}, "_ODataClient._upsert": {"executed_lines": [295, 296, 297, 298, 299], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 269}, "_ODataClient._upsert_multiple": {"executed_lines": [332, 333, 336, 337, 338, 339, 340, 341, 342, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354], "summary": {"covered_lines": 19, "num_statements": 19, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 301}, "_ODataClient._primary_id_attr": {"executed_lines": [359, 360, 361, 362, 364, 365, 366, 367, 368], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 357}, "_ODataClient._update_by_ids": {"executed_lines": [387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407], "summary": {"covered_lines": 21, "num_statements": 21, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 372}, "_ODataClient._delete_multiple": {"executed_lines": [424, 425, 426, 427, 431, 432, 433, 434, 435, 436, 437, 438], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 409}, "_ODataClient._update": {"executed_lines": [452], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 440}, "_ODataClient._update_multiple": {"executed_lines": [472, 473, 474, 475], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 454}, "_ODataClient._delete": {"executed_lines": [488], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 477}, "_ODataClient._get": {"executed_lines": [503], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 490}, "_ODataClient._get_multiple": {"executed_lines": [542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 553, 561, 562, 563, 564, 566, 567, 569, 570, 572, 573, 575, 576, 577, 578, 579, 581, 582, 583, 584, 586, 587, 588, 590, 591, 592, 593, 594, 595], "summary": {"covered_lines": 39, "num_statements": 39, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 505}, "_ODataClient._get_multiple._do_request": {"executed_lines": [554, 555, 556, 557, 558, 559], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 553}, "_ODataClient._query_sql": {"executed_lines": [617, 618, 619, 620, 621, 626, 628, 629, 630, 631, 632, 635, 636, 637, 638, 639, 641, 642, 643, 646, 647, 648, 649, 650, 652, 653, 662, 663, 667, 668, 669, 670, 679, 680, 681, 682, 683, 684, 691, 692, 693, 694, 695, 702, 703, 704, 705, 706, 707, 708, 709, 710, 712], "summary": {"covered_lines": 53, "num_statements": 53, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 598}, "_ODataClient._entity_set_from_schema_name": {"executed_lines": [720, 721, 724, 725, 726, 727, 728, 730, 731, 732, 736, 737, 738, 739, 740, 741, 742, 743, 748, 752, 753, 754, 755, 759, 760, 761, 762, 763], "summary": {"covered_lines": 28, "num_statements": 28, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 715}, "_ODataClient._get_entity_by_table_schema_name": {"executed_lines": [777, 779, 780, 781, 785, 786, 787], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 766}, "_ODataClient._create_entity": {"executed_lines": [796, 797, 809, 810, 811, 812, 813, 817, 818, 821, 822, 823], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 789}, "_ODataClient._get_attribute_metadata": {"executed_lines": [832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843, 844, 845, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859], "summary": {"covered_lines": 25, "num_statements": 25, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 825}, "_ODataClient._list_columns": {"executed_lines": [889, 890, 891, 895, 896, 897, 898, 899, 900, 901, 902, 903], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 861}, "_ODataClient._wait_for_attribute_visibility": {"executed_lines": [918, 919, 920, 921, 923, 924, 925, 926, 927, 928, 929, 930, 931, 934], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 905}, "_ODataClient._request_metadata_with_retry": {"executed_lines": [941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 939}, "_ODataClient._bulk_fetch_picklists": {"executed_lines": [962, 963, 964, 965, 966, 968, 969, 974, 975, 976, 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, 990, 992, 993, 995, 996, 997, 998, 999, 1000, 1001, 1002, 1003, 1004, 1006], "summary": {"covered_lines": 36, "num_statements": 38, "percent_covered": 94.73684210526316, "percent_covered_display": "95", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 94.73684210526316, "percent_statements_covered_display": "95"}, "missing_lines": [991, 994], "excluded_lines": [], "start_line": 954}, "_ODataClient._convert_labels_to_ints": {"executed_lines": [1017, 1020, 1024, 1025, 1028, 1031, 1032, 1033, 1035, 1037, 1038, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1049, 1050], "summary": {"covered_lines": 23, "num_statements": 24, "percent_covered": 95.83333333333333, "percent_covered_display": "96", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 95.83333333333333, "percent_statements_covered_display": "96"}, "missing_lines": [1034], "excluded_lines": [], "start_line": 1008}, "_ODataClient._get_table_info": {"executed_lines": [1061, 1062, 1063, 1064], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1052}, "_ODataClient._list_tables": {"executed_lines": [1101], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1074}, "_ODataClient._delete_table": {"executed_lines": [1115, 1116, 1117, 1121], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1103}, "_ODataClient._create_alternate_key": {"executed_lines": [1152, 1153, 1154, 1159, 1160, 1161, 1165, 1166, 1167, 1168, 1170], "summary": {"covered_lines": 11, "num_statements": 11, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1125}, "_ODataClient._get_alternate_keys": {"executed_lines": [1190, 1191, 1192, 1197, 1198, 1199, 1200], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1176}, "_ODataClient._delete_alternate_key": {"executed_lines": [1218, 1219, 1220, 1225, 1226, 1227], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1202}, "_ODataClient._create_table": {"executed_lines": [1259, 1260, 1261, 1266, 1270, 1271, 1273, 1277, 1278, 1279, 1280, 1281, 1282, 1283, 1284, 1286, 1287, 1288, 1289, 1290, 1292, 1293, 1294, 1296, 1303], "summary": {"covered_lines": 25, "num_statements": 25, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1229}, "_ODataClient._create_columns": {"executed_lines": [1333, 1334, 1336, 1337, 1338, 1343, 1344, 1345, 1347, 1348, 1349, 1350, 1354, 1355, 1356, 1361, 1362, 1364, 1365, 1367], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1313}, "_ODataClient._delete_columns": {"executed_lines": [1390, 1391, 1392, 1393, 1395, 1397, 1398, 1399, 1401, 1402, 1403, 1409, 1410, 1411, 1412, 1414, 1415, 1416, 1417, 1422, 1423, 1424, 1426, 1428, 1429, 1430, 1431, 1432, 1434, 1436, 1437, 1439], "summary": {"covered_lines": 32, "num_statements": 32, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1369}, "_ODataClient._build_create": {"executed_lines": [1452, 1453, 1454], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1443}, "_ODataClient._build_create_multiple": {"executed_lines": [1468, 1470, 1471, 1472, 1473, 1474, 1475, 1476, 1477, 1478], "summary": {"covered_lines": 10, "num_statements": 11, "percent_covered": 90.9090909090909, "percent_covered_display": "91", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 90.9090909090909, "percent_statements_covered_display": "91"}, "missing_lines": [1469], "excluded_lines": [], "start_line": 1461}, "_ODataClient._build_update": {"executed_lines": [1497, 1498, 1499, 1502, 1503, 1504], "summary": {"covered_lines": 6, "num_statements": 7, "percent_covered": 85.71428571428571, "percent_covered_display": "86", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 85.71428571428571, "percent_statements_covered_display": "86"}, "missing_lines": [1500], "excluded_lines": [], "start_line": 1484}, "_ODataClient._build_update_multiple_from_records": {"executed_lines": [1524, 1525, 1526, 1527, 1528, 1529, 1530, 1531, 1532], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1512}, "_ODataClient._build_update_multiple": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 9, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 9, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1546, 1547, 1548, 1549, 1550, 1551, 1555, 1557, 1558], "excluded_lines": [], "start_line": 1538}, "_ODataClient._build_upsert": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 5, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 5, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [1572, 1573, 1574, 1575, 1576], "excluded_lines": [], "start_line": 1560}, "_ODataClient._build_upsert_multiple": {"executed_lines": [1590, 1591, 1595, 1596, 1597, 1598, 1599, 1600, 1601, 1604, 1605, 1609, 1610, 1611, 1612, 1613, 1614], "summary": {"covered_lines": 17, "num_statements": 17, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1582}, "_ODataClient._build_delete": {"executed_lines": [1631, 1634, 1635, 1636], "summary": {"covered_lines": 4, "num_statements": 5, "percent_covered": 80.0, "percent_covered_display": "80", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 80.0, "percent_statements_covered_display": "80"}, "missing_lines": [1632], "excluded_lines": [], "start_line": 1620}, "_ODataClient._build_delete_multiple": {"executed_lines": [1645, 1646, 1647, 1648, 1679], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1643}, "_ODataClient._build_get": {"executed_lines": [1693, 1694, 1695, 1696, 1697], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1685}, "_ODataClient._build_sql": {"executed_lines": [1714, 1715, 1716], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1699}, "": {"executed_lines": [6, 8, 10, 11, 12, 13, 14, 16, 18, 19, 20, 21, 22, 23, 36, 46, 49, 80, 90, 103, 111, 114, 179, 193, 225, 269, 301, 357, 372, 409, 440, 454, 477, 490, 505, 598, 715, 766, 789, 825, 861, 905, 939, 954, 1008, 1052, 1074, 1103, 1125, 1176, 1202, 1229, 1313, 1369, 1443, 1461, 1484, 1512, 1538, 1560, 1582, 1620, 1643, 1685, 1699], "summary": {"covered_lines": 65, "num_statements": 65, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_ODataClient": {"executed_lines": [70, 71, 72, 86, 87, 88, 92, 93, 94, 104, 105, 106, 107, 108, 109, 112, 115, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 140, 141, 142, 143, 144, 145, 146, 147, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 185, 186, 187, 188, 189, 190, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 241, 242, 243, 244, 245, 246, 247, 248, 249, 251, 252, 253, 255, 256, 258, 259, 260, 262, 263, 264, 265, 266, 267, 295, 296, 297, 298, 299, 332, 333, 336, 337, 338, 339, 340, 341, 342, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 359, 360, 361, 362, 364, 365, 366, 367, 368, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 424, 425, 426, 427, 431, 432, 433, 434, 435, 436, 437, 438, 452, 472, 473, 474, 475, 488, 503, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 553, 554, 555, 556, 557, 558, 559, 561, 562, 563, 564, 566, 567, 569, 570, 572, 573, 575, 576, 577, 578, 579, 581, 582, 583, 584, 586, 587, 588, 590, 591, 592, 593, 594, 595, 617, 618, 619, 620, 621, 626, 628, 629, 630, 631, 632, 635, 636, 637, 638, 639, 641, 642, 643, 646, 647, 648, 649, 650, 652, 653, 662, 663, 667, 668, 669, 670, 679, 680, 681, 682, 683, 684, 691, 692, 693, 694, 695, 702, 703, 704, 705, 706, 707, 708, 709, 710, 712, 720, 721, 724, 725, 726, 727, 728, 730, 731, 732, 736, 737, 738, 739, 740, 741, 742, 743, 748, 752, 753, 754, 755, 759, 760, 761, 762, 763, 777, 779, 780, 781, 785, 786, 787, 796, 797, 809, 810, 811, 812, 813, 817, 818, 821, 822, 823, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843, 844, 845, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859, 889, 890, 891, 895, 896, 897, 898, 899, 900, 901, 902, 903, 918, 919, 920, 921, 923, 924, 925, 926, 927, 928, 929, 930, 931, 934, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 962, 963, 964, 965, 966, 968, 969, 974, 975, 976, 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, 990, 992, 993, 995, 996, 997, 998, 999, 1000, 1001, 1002, 1003, 1004, 1006, 1017, 1020, 1024, 1025, 1028, 1031, 1032, 1033, 1035, 1037, 1038, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1049, 1050, 1061, 1062, 1063, 1064, 1101, 1115, 1116, 1117, 1121, 1152, 1153, 1154, 1159, 1160, 1161, 1165, 1166, 1167, 1168, 1170, 1190, 1191, 1192, 1197, 1198, 1199, 1200, 1218, 1219, 1220, 1225, 1226, 1227, 1259, 1260, 1261, 1266, 1270, 1271, 1273, 1277, 1278, 1279, 1280, 1281, 1282, 1283, 1284, 1286, 1287, 1288, 1289, 1290, 1292, 1293, 1294, 1296, 1303, 1333, 1334, 1336, 1337, 1338, 1343, 1344, 1345, 1347, 1348, 1349, 1350, 1354, 1355, 1356, 1361, 1362, 1364, 1365, 1367, 1390, 1391, 1392, 1393, 1395, 1397, 1398, 1399, 1401, 1402, 1403, 1409, 1410, 1411, 1412, 1414, 1415, 1416, 1417, 1422, 1423, 1424, 1426, 1428, 1429, 1430, 1431, 1432, 1434, 1436, 1437, 1439, 1452, 1453, 1454, 1468, 1470, 1471, 1472, 1473, 1474, 1475, 1476, 1477, 1478, 1497, 1498, 1499, 1502, 1503, 1504, 1524, 1525, 1526, 1527, 1528, 1529, 1530, 1531, 1532, 1590, 1591, 1595, 1596, 1597, 1598, 1599, 1600, 1601, 1604, 1605, 1609, 1610, 1611, 1612, 1613, 1614, 1631, 1634, 1635, 1636, 1645, 1646, 1647, 1648, 1679, 1693, 1694, 1695, 1696, 1697, 1714, 1715, 1716], "summary": {"covered_lines": 605, "num_statements": 625, "percent_covered": 96.8, "percent_covered_display": "97", "missing_lines": 20, "excluded_lines": 0, "percent_statements_covered": 96.8, "percent_statements_covered_display": "97"}, "missing_lines": [991, 994, 1034, 1469, 1500, 1546, 1547, 1548, 1549, 1550, 1551, 1555, 1557, 1558, 1572, 1573, 1574, 1575, 1576, 1632], "excluded_lines": [], "start_line": 46}, "": {"executed_lines": [6, 8, 10, 11, 12, 13, 14, 16, 18, 19, 20, 21, 22, 23, 36, 46, 49, 80, 90, 103, 111, 114, 179, 193, 225, 269, 301, 357, 372, 409, 440, 454, 477, 490, 505, 598, 715, 766, 789, 825, 861, 905, 939, 954, 1008, 1052, 1074, 1103, 1125, 1176, 1202, 1229, 1313, 1369, 1443, 1461, 1484, 1512, 1538, 1560, 1582, 1620, 1643, 1685, 1699], "summary": {"covered_lines": 65, "num_statements": 65, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\data\\_odata_base.py": {"executed_lines": [10, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24, 26, 27, 33, 38, 39, 40, 42, 44, 45, 46, 47, 50, 60, 61, 62, 63, 64, 70, 71, 72, 73, 74, 75, 78, 79, 82, 83, 84, 85, 86, 88, 89, 98, 99, 100, 101, 102, 103, 112, 120, 130, 131, 133, 134, 140, 142, 143, 144, 145, 146, 155, 156, 158, 160, 161, 163, 165, 166, 179, 180, 181, 183, 184, 189, 190, 191, 193, 194, 202, 203, 205, 206, 207, 208, 209, 210, 216, 217, 219, 220, 221, 222, 224, 226, 227, 228, 229, 231, 233, 235, 237, 238, 239, 240, 241, 243, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 270, 271, 273, 274, 275, 286, 287, 288, 290, 292, 295, 296, 298, 299, 305, 310, 311, 312, 313, 314, 315, 316, 323, 324, 325, 330, 343, 344, 345, 348, 349, 350, 352, 353, 356, 358, 360, 361, 362, 364, 365, 366, 367, 368, 370, 371, 372, 373, 374, 375, 376, 377, 379, 380, 381, 382, 383, 384, 386, 387, 388, 389, 391, 393, 395, 396, 397, 398, 399, 400, 401, 409, 410, 423, 427, 428, 429, 430, 433, 434, 435, 436, 445, 446, 455, 456, 465, 466, 475, 476, 485, 486, 494, 495, 513, 514, 520, 526, 535, 536, 538, 539, 540, 541, 542, 543, 547, 548, 549, 550, 551, 552, 564, 565, 566, 567, 573, 575, 581, 583, 584, 593, 600, 601, 602, 604, 605, 606, 607, 608, 609, 610, 612, 631, 637, 643, 644, 668, 669, 671, 683, 684, 687, 688, 695, 697, 714, 722, 735, 739, 740, 741, 746, 750, 751, 752, 753, 761, 766, 800, 801, 802, 810, 811, 812, 820, 821, 829, 830, 837, 838, 845, 846, 858, 859, 869, 870, 880, 881, 891, 897, 902, 903, 904, 905, 913, 925, 926, 927, 932, 933, 934], "summary": {"covered_lines": 312, "num_statements": 329, "percent_covered": 94.83282674772036, "percent_covered_display": "95", "missing_lines": 17, "excluded_lines": 0, "percent_statements_covered": 94.83282674772036, "percent_statements_covered_display": "95"}, "missing_lines": [132, 147, 149, 293, 619, 620, 621, 625, 704, 705, 706, 707, 716, 724, 725, 906, 907], "excluded_lines": [], "functions": {"_extract_pagingcookie": {"executed_lines": [60, 61, 62, 63, 64, 70, 71, 72, 73, 74, 75], "summary": {"covered_lines": 11, "num_statements": 11, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 50}, "_RequestContext.build": {"executed_lines": [98, 99, 100, 101, 102, 103], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 89}, "_ODataBase.__init__": {"executed_lines": [130, 131, 133, 134, 140, 142, 143, 144, 145, 146], "summary": {"covered_lines": 10, "num_statements": 13, "percent_covered": 76.92307692307692, "percent_covered_display": "77", "missing_lines": 3, "excluded_lines": 0, "percent_statements_covered": 76.92307692307692, "percent_statements_covered_display": "77"}, "missing_lines": [132, 147, 149], "excluded_lines": [], "start_line": 120}, "_ODataBase._escape_odata_quotes": {"executed_lines": [158], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 156}, "_ODataBase._normalize_cache_key": {"executed_lines": [163], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 161}, "_ODataBase._lowercase_keys": {"executed_lines": [179, 180, 181], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 166}, "_ODataBase._lowercase_list": {"executed_lines": [189, 190, 191], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 184}, "_ODataBase._extract_logical_table": {"executed_lines": [202, 203, 205, 206, 207, 208, 209, 210], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 194}, "_ODataBase._call_scope": {"executed_lines": [219, 220, 221, 222, 224], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 217}, "_ODataBase._format_key": {"executed_lines": [227, 228, 229, 231, 233, 237, 238, 239, 240, 241], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 226}, "_ODataBase._format_key.esc": {"executed_lines": [235], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 233}, "_ODataBase._build_alternate_key_str": {"executed_lines": [258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 270, 271], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 243}, "_ODataBase._label": {"executed_lines": [274, 275], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 273}, "_ODataBase._to_pascal": {"executed_lines": [287, 288], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 286}, "_ODataBase._normalize_picklist_label": {"executed_lines": [292, 295, 296, 298, 299], "summary": {"covered_lines": 5, "num_statements": 6, "percent_covered": 83.33333333333333, "percent_covered_display": "83", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 83.33333333333333, "percent_statements_covered_display": "83"}, "missing_lines": [293], "excluded_lines": [], "start_line": 290}, "_ODataBase._build_localizedlabels_payload": {"executed_lines": [310, 311, 312, 313, 314, 315, 316, 323, 324, 325], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 305}, "_ODataBase._enum_optionset_payload": {"executed_lines": [343, 344, 345, 348, 349, 350, 352, 353, 356, 358, 360, 361, 362, 364, 365, 366, 367, 368, 370, 371, 372, 373, 374, 375, 376, 377, 379, 380, 381, 382, 383, 384, 386, 387, 388, 389, 391, 393, 395, 396, 397, 398, 399, 400, 401, 409, 410], "summary": {"covered_lines": 47, "num_statements": 47, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 330}, "_ODataBase._attribute_payload": {"executed_lines": [427, 428, 429, 430, 433, 434, 435, 436, 445, 446, 455, 456, 465, 466, 475, 476, 485, 486, 494, 495, 513, 514, 520], "summary": {"covered_lines": 23, "num_statements": 23, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 423}, "_ODataBase._build_create_entity": {"executed_lines": [535, 536, 538, 539, 540, 541, 542, 543, 547, 548, 549, 550, 551, 552, 564, 565, 566, 567], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 526}, "_ODataBase._build_delete_entity": {"executed_lines": [575], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 573}, "_ODataBase._build_get_entity": {"executed_lines": [583, 584], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 581}, "_ODataBase._build_list_entities": {"executed_lines": [600, 601, 602, 604, 605, 606, 607, 608, 609, 610], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 593}, "_ODataBase._build_create_column": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 4, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [619, 620, 621, 625], "excluded_lines": [], "start_line": 612}, "_ODataBase._build_delete_column": {"executed_lines": [637], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 631}, "_ODataBase._build_lookup_field_models": {"executed_lines": [668, 669, 671, 683, 684, 687, 688, 695], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 644}, "_ODataBase._build_create_relationship": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 4, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 4, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [704, 705, 706, 707], "excluded_lines": [], "start_line": 697}, "_ODataBase._build_delete_relationship": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [716], "excluded_lines": [], "start_line": 714}, "_ODataBase._build_get_relationship": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [724, 725], "excluded_lines": [], "start_line": 722}, "_ODataBase._sql_guardrails": {"executed_lines": [800, 801, 802, 810, 811, 812, 820, 821, 829, 830, 837, 838, 845, 846, 858, 859, 869, 870, 880, 881, 891], "summary": {"covered_lines": 21, "num_statements": 21, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 766}, "_ODataBase.close": {"executed_lines": [902, 903, 904, 905], "summary": {"covered_lines": 4, "num_statements": 6, "percent_covered": 66.66666666666667, "percent_covered_display": "67", "missing_lines": 2, "excluded_lines": 0, "percent_statements_covered": 66.66666666666667, "percent_statements_covered_display": "67"}, "missing_lines": [906, 907], "excluded_lines": [], "start_line": 897}, "_ODataBase._flush_cache": {"executed_lines": [925, 926, 927, 932, 933, 934], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 913}, "": {"executed_lines": [10, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24, 26, 27, 33, 38, 39, 40, 42, 44, 45, 46, 47, 50, 78, 79, 82, 83, 84, 85, 86, 88, 89, 112, 120, 155, 156, 160, 161, 165, 166, 183, 184, 193, 194, 216, 217, 226, 243, 273, 286, 290, 305, 330, 423, 526, 573, 581, 593, 612, 631, 643, 644, 697, 714, 722, 735, 739, 740, 741, 746, 750, 751, 752, 753, 761, 766, 897, 913], "summary": {"covered_lines": 80, "num_statements": 80, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_RequestContext": {"executed_lines": [98, 99, 100, 101, 102, 103], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 79}, "_ODataBase": {"executed_lines": [130, 131, 133, 134, 140, 142, 143, 144, 145, 146, 158, 163, 179, 180, 181, 189, 190, 191, 202, 203, 205, 206, 207, 208, 209, 210, 219, 220, 221, 222, 224, 227, 228, 229, 231, 233, 235, 237, 238, 239, 240, 241, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 270, 271, 274, 275, 287, 288, 292, 295, 296, 298, 299, 310, 311, 312, 313, 314, 315, 316, 323, 324, 325, 343, 344, 345, 348, 349, 350, 352, 353, 356, 358, 360, 361, 362, 364, 365, 366, 367, 368, 370, 371, 372, 373, 374, 375, 376, 377, 379, 380, 381, 382, 383, 384, 386, 387, 388, 389, 391, 393, 395, 396, 397, 398, 399, 400, 401, 409, 410, 427, 428, 429, 430, 433, 434, 435, 436, 445, 446, 455, 456, 465, 466, 475, 476, 485, 486, 494, 495, 513, 514, 520, 535, 536, 538, 539, 540, 541, 542, 543, 547, 548, 549, 550, 551, 552, 564, 565, 566, 567, 575, 583, 584, 600, 601, 602, 604, 605, 606, 607, 608, 609, 610, 637, 668, 669, 671, 683, 684, 687, 688, 695, 800, 801, 802, 810, 811, 812, 820, 821, 829, 830, 837, 838, 845, 846, 858, 859, 869, 870, 880, 881, 891, 902, 903, 904, 905, 925, 926, 927, 932, 933, 934], "summary": {"covered_lines": 215, "num_statements": 232, "percent_covered": 92.67241379310344, "percent_covered_display": "93", "missing_lines": 17, "excluded_lines": 0, "percent_statements_covered": 92.67241379310344, "percent_statements_covered_display": "93"}, "missing_lines": [132, 147, 149, 293, 619, 620, 621, 625, 704, 705, 706, 707, 716, 724, 725, 906, 907], "excluded_lines": [], "start_line": 112}, "": {"executed_lines": [10, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24, 26, 27, 33, 38, 39, 40, 42, 44, 45, 46, 47, 50, 60, 61, 62, 63, 64, 70, 71, 72, 73, 74, 75, 78, 79, 82, 83, 84, 85, 86, 88, 89, 112, 120, 155, 156, 160, 161, 165, 166, 183, 184, 193, 194, 216, 217, 226, 243, 273, 286, 290, 305, 330, 423, 526, 573, 581, 593, 612, 631, 643, 644, 697, 714, 722, 735, 739, 740, 741, 746, 750, 751, 752, 753, 761, 766, 897, 913], "summary": {"covered_lines": 91, "num_statements": 91, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\data\\_raw_request.py": {"executed_lines": [6, 8, 9, 11, 14, 15, 29, 30, 31, 32, 33], "summary": {"covered_lines": 11, "num_statements": 11, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [6, 8, 9, 11, 14, 15, 29, 30, 31, 32, 33], "summary": {"covered_lines": 11, "num_statements": 11, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_RawRequest": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 15}, "": {"executed_lines": [6, 8, 9, 11, 14, 15, 29, 30, 31, 32, 33], "summary": {"covered_lines": 11, "num_statements": 11, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\data\\_relationships.py": {"executed_lines": [10, 12, 14, 15, 18, 28, 51, 54, 55, 57, 58, 59, 61, 64, 66, 74, 94, 96, 98, 99, 100, 102, 105, 107, 114, 123, 124, 125, 126, 128, 140, 141, 142, 143, 144, 145, 147, 171, 172, 173, 174, 175, 176, 177, 178, 180, 210, 211, 213, 214, 215, 220, 228, 229, 230, 231, 232, 233, 234, 236, 237, 238, 240, 241, 242, 244, 246, 256, 257, 258, 259], "summary": {"covered_lines": 71, "num_statements": 71, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"_RelationshipOperationsMixin._create_one_to_many_relationship": {"executed_lines": [51, 54, 55, 57, 58, 59, 61, 64, 66], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 28}, "_RelationshipOperationsMixin._create_many_to_many_relationship": {"executed_lines": [94, 96, 98, 99, 100, 102, 105, 107], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 74}, "_RelationshipOperationsMixin._delete_relationship": {"executed_lines": [123, 124, 125, 126], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 114}, "_RelationshipOperationsMixin._get_relationship": {"executed_lines": [140, 141, 142, 143, 144, 145], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 128}, "_RelationshipOperationsMixin._list_relationships": {"executed_lines": [171, 172, 173, 174, 175, 176, 177, 178], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 147}, "_RelationshipOperationsMixin._list_table_relationships": {"executed_lines": [210, 211, 213, 214, 215, 220, 228, 229, 230, 231, 232, 233, 234, 236, 237, 238, 240, 241, 242, 244], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 180}, "_RelationshipOperationsMixin._extract_id_from_header": {"executed_lines": [256, 257, 258, 259], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 246}, "": {"executed_lines": [10, 12, 14, 15, 18, 28, 74, 114, 128, 147, 180, 246], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_RelationshipOperationsMixin": {"executed_lines": [51, 54, 55, 57, 58, 59, 61, 64, 66, 94, 96, 98, 99, 100, 102, 105, 107, 123, 124, 125, 126, 140, 141, 142, 143, 144, 145, 171, 172, 173, 174, 175, 176, 177, 178, 210, 211, 213, 214, 215, 220, 228, 229, 230, 231, 232, 233, 234, 236, 237, 238, 240, 241, 242, 244, 256, 257, 258, 259], "summary": {"covered_lines": 59, "num_statements": 59, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 18}, "": {"executed_lines": [10, 12, 14, 15, 18, 28, 74, 114, 128, 147, 180, 246], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\data\\_upload.py": {"executed_lines": [6, 8, 11, 14, 43, 46, 49, 50, 51, 52, 53, 54, 56, 59, 61, 63, 64, 65, 66, 67, 70, 72, 73, 76, 77, 78, 80, 90, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 109, 110, 112, 114, 115, 117, 147, 148, 150, 151, 152, 153, 154, 155, 156, 157, 158, 161, 162, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 193, 194, 195], "summary": {"covered_lines": 87, "num_statements": 87, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"_FileUploadMixin._upload_file": {"executed_lines": [43, 46, 49, 50, 51, 52, 53, 54, 56, 59, 61, 63, 64, 65, 66, 67, 70, 72, 73, 76, 77, 78], "summary": {"covered_lines": 22, "num_statements": 22, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 14}, "_FileUploadMixin._upload_file_small": {"executed_lines": [90, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 109, 110, 112, 114, 115], "summary": {"covered_lines": 20, "num_statements": 20, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 80}, "_FileUploadMixin._upload_file_chunk": {"executed_lines": [147, 148, 150, 151, 152, 153, 154, 155, 156, 157, 158, 161, 162, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 193, 194, 195], "summary": {"covered_lines": 39, "num_statements": 39, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 117}, "": {"executed_lines": [6, 8, 11, 14, 80, 117], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"_FileUploadMixin": {"executed_lines": [43, 46, 49, 50, 51, 52, 53, 54, 56, 59, 61, 63, 64, 65, 66, 67, 70, 72, 73, 76, 77, 78, 90, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 109, 110, 112, 114, 115, 147, 148, 150, 151, 152, 153, 154, 155, 156, 157, 158, 161, 162, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 193, 194, 195], "summary": {"covered_lines": 81, "num_statements": 81, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 11}, "": {"executed_lines": [6, 8, 11, 14, 80, 117], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\extensions\\__init__.py": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [9], "excluded_lines": [], "functions": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [9], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 0.0, "percent_statements_covered_display": "0"}, "missing_lines": [9], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\models\\__init__.py": {"executed_lines": [19], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [19], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [19], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\models\\batch.py": {"executed_lines": [6, 8, 9, 11, 14, 15, 39, 40, 41, 42, 43, 44, 46, 47, 49, 52, 53, 71, 73, 74, 76, 78, 79, 81, 83, 84, 86, 88, 89, 106], "summary": {"covered_lines": 30, "num_statements": 30, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"BatchItemResponse.is_success": {"executed_lines": [49], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 47}, "BatchResult.succeeded": {"executed_lines": [76], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 74}, "BatchResult.failed": {"executed_lines": [81], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 79}, "BatchResult.has_errors": {"executed_lines": [86], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 84}, "BatchResult.entity_ids": {"executed_lines": [106], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 89}, "": {"executed_lines": [6, 8, 9, 11, 14, 15, 39, 40, 41, 42, 43, 44, 46, 47, 52, 53, 71, 73, 74, 78, 79, 83, 84, 88, 89], "summary": {"covered_lines": 25, "num_statements": 25, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"BatchItemResponse": {"executed_lines": [49], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 15}, "BatchResult": {"executed_lines": [76, 81, 86, 106], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 53}, "": {"executed_lines": [6, 8, 9, 11, 14, 15, 39, 40, 41, 42, 43, 44, 46, 47, 52, 53, 71, 73, 74, 78, 79, 83, 84, 88, 89], "summary": {"covered_lines": 25, "num_statements": 25, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\models\\filters.py": {"executed_lines": [34, 36, 37, 38, 39, 41, 67, 78, 79, 81, 82, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 95, 96, 97, 98, 99, 100, 101, 102, 103, 105, 113, 123, 125, 127, 128, 129, 130, 132, 133, 134, 135, 137, 138, 140, 141, 143, 144, 152, 155, 157, 158, 159, 160, 162, 163, 166, 169, 171, 172, 173, 174, 176, 177, 180, 183, 185, 186, 187, 189, 190, 193, 196, 198, 199, 200, 202, 203, 206, 209, 211, 212, 214, 215, 218, 221, 223, 224, 225, 226, 227, 229, 231, 232, 233, 236, 239, 241, 242, 243, 244, 245, 247, 249, 250, 251, 254, 257, 259, 260, 262, 263, 271, 282, 285, 292, 295, 302, 305, 312, 315, 322, 325, 332, 335, 342, 345, 352, 355, 362, 365, 380, 383, 389, 392, 398, 401, 416, 419, 434, 437, 452, 455, 465], "summary": {"covered_lines": 144, "num_statements": 144, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"_format_value": {"executed_lines": [78, 79, 81, 82, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 95, 96, 97, 98, 99, 100, 101, 102, 103, 105], "summary": {"covered_lines": 24, "num_statements": 24, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 67}, "FilterExpression.to_odata": {"executed_lines": [125], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 123}, "FilterExpression.__and__": {"executed_lines": [128, 129, 130], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 127}, "FilterExpression.__or__": {"executed_lines": [133, 134, 135], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 132}, "FilterExpression.__invert__": {"executed_lines": [138], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 137}, "FilterExpression.__str__": {"executed_lines": [141], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 140}, "FilterExpression.__repr__": {"executed_lines": [144], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 143}, "_ComparisonFilter.__init__": {"executed_lines": [158, 159, 160], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 157}, "_ComparisonFilter.to_odata": {"executed_lines": [163], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 162}, "_FunctionFilter.__init__": {"executed_lines": [172, 173, 174], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 171}, "_FunctionFilter.to_odata": {"executed_lines": [177], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 176}, "_AndFilter.__init__": {"executed_lines": [186, 187], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 185}, "_AndFilter.to_odata": {"executed_lines": [190], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 189}, "_OrFilter.__init__": {"executed_lines": [199, 200], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 198}, "_OrFilter.to_odata": {"executed_lines": [203], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 202}, "_NotFilter.__init__": {"executed_lines": [212], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 211}, "_NotFilter.to_odata": {"executed_lines": [215], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 214}, "_InFilter.__init__": {"executed_lines": [224, 225, 226, 227], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 223}, "_InFilter.to_odata": {"executed_lines": [231, 232, 233], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 229}, "_NotInFilter.__init__": {"executed_lines": [242, 243, 244, 245], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 241}, "_NotInFilter.to_odata": {"executed_lines": [249, 250, 251], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 247}, "_RawFilter.__init__": {"executed_lines": [260], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 259}, "_RawFilter.to_odata": {"executed_lines": [263], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 262}, "eq": {"executed_lines": [282], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 271}, "ne": {"executed_lines": [292], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 285}, "gt": {"executed_lines": [302], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 295}, "ge": {"executed_lines": [312], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 305}, "lt": {"executed_lines": [322], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 315}, "le": {"executed_lines": [332], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 325}, "contains": {"executed_lines": [342], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 335}, "startswith": {"executed_lines": [352], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 345}, "endswith": {"executed_lines": [362], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 355}, "between": {"executed_lines": [380], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 365}, "is_null": {"executed_lines": [389], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 383}, "is_not_null": {"executed_lines": [398], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 392}, "filter_in": {"executed_lines": [416], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 401}, "not_in": {"executed_lines": [434], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 419}, "not_between": {"executed_lines": [452], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 437}, "raw": {"executed_lines": [465], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 455}, "": {"executed_lines": [34, 36, 37, 38, 39, 41, 67, 113, 123, 127, 132, 137, 140, 143, 152, 155, 157, 162, 166, 169, 171, 176, 180, 183, 185, 189, 193, 196, 198, 202, 206, 209, 211, 214, 218, 221, 223, 229, 236, 239, 241, 247, 254, 257, 259, 262, 271, 285, 295, 305, 315, 325, 335, 345, 355, 365, 383, 392, 401, 419, 437, 455], "summary": {"covered_lines": 62, "num_statements": 62, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"FilterExpression": {"executed_lines": [125, 128, 129, 130, 133, 134, 135, 138, 141, 144], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 113}, "_ComparisonFilter": {"executed_lines": [158, 159, 160, 163], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 152}, "_FunctionFilter": {"executed_lines": [172, 173, 174, 177], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 166}, "_AndFilter": {"executed_lines": [186, 187, 190], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 180}, "_OrFilter": {"executed_lines": [199, 200, 203], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 193}, "_NotFilter": {"executed_lines": [212, 215], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 206}, "_InFilter": {"executed_lines": [224, 225, 226, 227, 231, 232, 233], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 218}, "_NotInFilter": {"executed_lines": [242, 243, 244, 245, 249, 250, 251], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 236}, "_RawFilter": {"executed_lines": [260, 263], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 254}, "": {"executed_lines": [34, 36, 37, 38, 39, 41, 67, 78, 79, 81, 82, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 95, 96, 97, 98, 99, 100, 101, 102, 103, 105, 113, 123, 127, 132, 137, 140, 143, 152, 155, 157, 162, 166, 169, 171, 176, 180, 183, 185, 189, 193, 196, 198, 202, 206, 209, 211, 214, 218, 221, 223, 229, 236, 239, 241, 247, 254, 257, 259, 262, 271, 282, 285, 292, 295, 302, 305, 312, 315, 322, 325, 332, 335, 342, 345, 352, 355, 362, 365, 380, 383, 389, 392, 398, 401, 416, 419, 434, 437, 452, 455, 465], "summary": {"covered_lines": 102, "num_statements": 102, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\models\\labels.py": {"executed_lines": [6, 8, 9, 11, 17, 18, 31, 32, 33, 35, 49, 54, 55, 56, 59, 60, 73, 74, 75, 77, 93, 98, 99, 100, 101, 102, 103, 104, 107], "summary": {"covered_lines": 29, "num_statements": 29, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"LocalizedLabel.to_dict": {"executed_lines": [49, 54, 55, 56], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 35}, "Label.to_dict": {"executed_lines": [93, 98, 99, 100, 101, 102, 103, 104], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 77}, "": {"executed_lines": [6, 8, 9, 11, 17, 18, 31, 32, 33, 35, 59, 60, 73, 74, 75, 77, 107], "summary": {"covered_lines": 17, "num_statements": 17, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"LocalizedLabel": {"executed_lines": [49, 54, 55, 56], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 18}, "Label": {"executed_lines": [93, 98, 99, 100, 101, 102, 103, 104], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 60}, "": {"executed_lines": [6, 8, 9, 11, 17, 18, 31, 32, 33, 35, 59, 60, 73, 74, 75, 77, 107], "summary": {"covered_lines": 17, "num_statements": 17, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\models\\query_builder.py": {"executed_lines": [47, 49, 51, 53, 54, 56, 59, 66, 67, 68, 69, 70, 71, 72, 73, 74, 77, 102, 103, 104, 105, 106, 107, 109, 115, 116, 118, 124, 125, 127, 134, 135, 136, 138, 144, 145, 147, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 167, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 207, 220, 221, 225, 232, 233, 235, 242, 243, 245, 252, 253, 255, 262, 263, 265, 272, 273, 275, 282, 283, 287, 294, 295, 297, 304, 305, 307, 314, 315, 319, 325, 326, 328, 334, 335, 339, 353, 354, 356, 370, 371, 373, 386, 387, 389, 402, 403, 405, 425, 426, 430, 453, 454, 455, 456, 460, 469, 470, 471, 475, 482, 483, 484, 485, 487, 497, 498, 499, 500, 502, 518, 519, 521, 548, 549, 551, 574, 575, 579, 602, 603, 604, 606, 607, 611, 623, 624, 625, 626, 627, 628, 629, 630, 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645, 646, 650, 659, 660, 667, 713, 714, 718, 719, 720, 722, 734, 735, 737, 738, 739, 741, 745, 775, 776, 780, 781, 782], "summary": {"covered_lines": 195, "num_statements": 195, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"ExpandOption.__init__": {"executed_lines": [103, 104, 105, 106, 107], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 102}, "ExpandOption.select": {"executed_lines": [115, 116], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 109}, "ExpandOption.filter": {"executed_lines": [124, 125], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 118}, "ExpandOption.order_by": {"executed_lines": [134, 135, 136], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 127}, "ExpandOption.top": {"executed_lines": [144, 145], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 138}, "ExpandOption.to_odata": {"executed_lines": [153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 147}, "QueryBuilder.__init__": {"executed_lines": [191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 190}, "QueryBuilder.select": {"executed_lines": [220, 221], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 207}, "QueryBuilder.filter_eq": {"executed_lines": [232, 233], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 225}, "QueryBuilder.filter_ne": {"executed_lines": [242, 243], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 235}, "QueryBuilder.filter_gt": {"executed_lines": [252, 253], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 245}, "QueryBuilder.filter_ge": {"executed_lines": [262, 263], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 255}, "QueryBuilder.filter_lt": {"executed_lines": [272, 273], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 265}, "QueryBuilder.filter_le": {"executed_lines": [282, 283], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 275}, "QueryBuilder.filter_contains": {"executed_lines": [294, 295], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 287}, "QueryBuilder.filter_startswith": {"executed_lines": [304, 305], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 297}, "QueryBuilder.filter_endswith": {"executed_lines": [314, 315], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 307}, "QueryBuilder.filter_null": {"executed_lines": [325, 326], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 319}, "QueryBuilder.filter_not_null": {"executed_lines": [334, 335], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 328}, "QueryBuilder.filter_in": {"executed_lines": [353, 354], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 339}, "QueryBuilder.filter_not_in": {"executed_lines": [370, 371], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 356}, "QueryBuilder.filter_between": {"executed_lines": [386, 387], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 373}, "QueryBuilder.filter_not_between": {"executed_lines": [402, 403], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 389}, "QueryBuilder.filter_raw": {"executed_lines": [425, 426], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 405}, "QueryBuilder.where": {"executed_lines": [453, 454, 455, 456], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 430}, "QueryBuilder.order_by": {"executed_lines": [469, 470, 471], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 460}, "QueryBuilder.top": {"executed_lines": [482, 483, 484, 485], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 475}, "QueryBuilder.page_size": {"executed_lines": [497, 498, 499, 500], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 487}, "QueryBuilder.count": {"executed_lines": [518, 519], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 502}, "QueryBuilder.include_formatted_values": {"executed_lines": [548, 549], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 521}, "QueryBuilder.include_annotations": {"executed_lines": [574, 575], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 551}, "QueryBuilder.expand": {"executed_lines": [602, 603, 604, 606, 607], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 579}, "QueryBuilder.build": {"executed_lines": [623, 624, 625, 626, 627, 628, 629, 630, 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645, 646], "summary": {"covered_lines": 23, "num_statements": 23, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 611}, "QueryBuilder._validate_constraints": {"executed_lines": [659, 660], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 650}, "QueryBuilder.execute": {"executed_lines": [713, 714, 718, 719, 720, 722, 734, 735, 737, 741], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 667}, "QueryBuilder.execute._flat": {"executed_lines": [738, 739], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 737}, "QueryBuilder.to_dataframe": {"executed_lines": [775, 776, 780, 781, 782], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 745}, "": {"executed_lines": [47, 49, 51, 53, 54, 56, 59, 66, 67, 68, 69, 70, 71, 72, 73, 74, 77, 102, 109, 118, 127, 138, 147, 167, 190, 207, 225, 235, 245, 255, 265, 275, 287, 297, 307, 319, 328, 339, 356, 373, 389, 405, 430, 460, 475, 487, 502, 521, 551, 579, 611, 650, 667, 745], "summary": {"covered_lines": 54, "num_statements": 54, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"QueryParams": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 59}, "ExpandOption": {"executed_lines": [103, 104, 105, 106, 107, 115, 116, 124, 125, 134, 135, 136, 144, 145, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164], "summary": {"covered_lines": 26, "num_statements": 26, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 77}, "QueryBuilder": {"executed_lines": [191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 220, 221, 232, 233, 242, 243, 252, 253, 262, 263, 272, 273, 282, 283, 294, 295, 304, 305, 314, 315, 325, 326, 334, 335, 353, 354, 370, 371, 386, 387, 402, 403, 425, 426, 453, 454, 455, 456, 469, 470, 471, 482, 483, 484, 485, 497, 498, 499, 500, 518, 519, 548, 549, 574, 575, 602, 603, 604, 606, 607, 623, 624, 625, 626, 627, 628, 629, 630, 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645, 646, 659, 660, 713, 714, 718, 719, 720, 722, 734, 735, 737, 738, 739, 741, 775, 776, 780, 781, 782], "summary": {"covered_lines": 115, "num_statements": 115, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 167}, "": {"executed_lines": [47, 49, 51, 53, 54, 56, 59, 66, 67, 68, 69, 70, 71, 72, 73, 74, 77, 102, 109, 118, 127, 138, 147, 167, 190, 207, 225, 235, 245, 255, 265, 275, 287, 297, 307, 319, 328, 339, 356, 373, 389, 405, 430, 460, 475, 487, 502, 521, 551, 579, 611, 650, 667, 745], "summary": {"covered_lines": 54, "num_statements": 54, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\models\\record.py": {"executed_lines": [6, 8, 9, 11, 13, 16, 17, 41, 42, 43, 44, 48, 49, 51, 52, 54, 55, 57, 58, 60, 61, 63, 64, 66, 68, 70, 72, 74, 76, 78, 80, 84, 85, 106, 107, 108, 112, 114], "summary": {"covered_lines": 38, "num_statements": 38, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"Record.__getitem__": {"executed_lines": [49], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 48}, "Record.__setitem__": {"executed_lines": [52], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 51}, "Record.__delitem__": {"executed_lines": [55], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 54}, "Record.__contains__": {"executed_lines": [58], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 57}, "Record.__iter__": {"executed_lines": [61], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 60}, "Record.__len__": {"executed_lines": [64], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 63}, "Record.get": {"executed_lines": [68], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 66}, "Record.keys": {"executed_lines": [72], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 70}, "Record.values": {"executed_lines": [76], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 74}, "Record.items": {"executed_lines": [80], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 78}, "Record.from_api_response": {"executed_lines": [106, 107, 108], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 85}, "Record.to_dict": {"executed_lines": [114], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 112}, "": {"executed_lines": [6, 8, 9, 11, 13, 16, 17, 41, 42, 43, 44, 48, 51, 54, 57, 60, 63, 66, 70, 74, 78, 84, 85, 112], "summary": {"covered_lines": 24, "num_statements": 24, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"Record": {"executed_lines": [49, 52, 55, 58, 61, 64, 68, 72, 76, 80, 106, 107, 108, 114], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 17}, "": {"executed_lines": [6, 8, 9, 11, 13, 16, 17, 41, 42, 43, 44, 48, 51, 54, 57, 60, 63, 66, 70, 74, 78, 84, 85, 112], "summary": {"covered_lines": 24, "num_statements": 24, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\models\\relationship.py": {"executed_lines": [6, 8, 9, 11, 20, 23, 24, 52, 53, 54, 55, 56, 57, 58, 60, 77, 85, 86, 87, 90, 91, 116, 117, 118, 119, 120, 122, 142, 154, 155, 156, 157, 158, 161, 162, 185, 186, 187, 188, 189, 190, 191, 193, 215, 223, 224, 225, 226, 227, 230, 231, 251, 252, 253, 254, 255, 257, 278, 279, 286, 287, 288, 291, 292, 324, 325, 326, 329, 330, 331, 334, 335, 337, 338, 361, 370, 371, 391, 399, 400, 414, 415, 416, 418, 419, 429, 430, 437, 440], "summary": {"covered_lines": 89, "num_statements": 89, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"CascadeConfiguration.to_dict": {"executed_lines": [77, 85, 86, 87], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 60}, "LookupAttributeMetadata.to_dict": {"executed_lines": [142, 154, 155, 156, 157, 158], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 122}, "OneToManyRelationshipMetadata.to_dict": {"executed_lines": [215, 223, 224, 225, 226, 227], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 193}, "ManyToManyRelationshipMetadata.to_dict": {"executed_lines": [278, 279, 286, 287, 288], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 257}, "RelationshipInfo.from_one_to_many": {"executed_lines": [361], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 338}, "RelationshipInfo.from_many_to_many": {"executed_lines": [391], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 371}, "RelationshipInfo.from_api_response": {"executed_lines": [414, 415, 416, 418, 419, 429, 430, 437], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 400}, "": {"executed_lines": [6, 8, 9, 11, 20, 23, 24, 52, 53, 54, 55, 56, 57, 58, 60, 90, 91, 116, 117, 118, 119, 120, 122, 161, 162, 185, 186, 187, 188, 189, 190, 191, 193, 230, 231, 251, 252, 253, 254, 255, 257, 291, 292, 324, 325, 326, 329, 330, 331, 334, 335, 337, 338, 370, 371, 399, 400, 440], "summary": {"covered_lines": 58, "num_statements": 58, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"CascadeConfiguration": {"executed_lines": [77, 85, 86, 87], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 24}, "LookupAttributeMetadata": {"executed_lines": [142, 154, 155, 156, 157, 158], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 91}, "OneToManyRelationshipMetadata": {"executed_lines": [215, 223, 224, 225, 226, 227], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 162}, "ManyToManyRelationshipMetadata": {"executed_lines": [278, 279, 286, 287, 288], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 231}, "RelationshipInfo": {"executed_lines": [361, 391, 414, 415, 416, 418, 419, 429, 430, 437], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 292}, "": {"executed_lines": [6, 8, 9, 11, 20, 23, 24, 52, 53, 54, 55, 56, 57, 58, 60, 90, 91, 116, 117, 118, 119, 120, 122, 161, 162, 185, 186, 187, 188, 189, 190, 191, 193, 230, 231, 251, 252, 253, 254, 255, 257, 291, 292, 324, 325, 326, 329, 330, 331, 334, 335, 337, 338, 370, 371, 399, 400, 440], "summary": {"covered_lines": 58, "num_statements": 58, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\models\\table_info.py": {"executed_lines": [6, 8, 9, 11, 14, 15, 36, 37, 38, 39, 40, 41, 42, 43, 45, 46, 54, 55, 56, 59, 60, 61, 64, 65, 67, 79, 80, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 123, 135, 137, 139, 140, 141, 142, 143, 145, 146, 147, 148, 149, 151, 152, 154, 155, 157, 159, 160, 161, 162, 164, 166, 168, 170, 172, 174, 178, 179, 189, 199, 200, 208, 209, 210, 213, 214, 215, 217, 230, 232, 235, 236, 249, 250, 251, 252, 254, 255, 262], "summary": {"covered_lines": 88, "num_statements": 88, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"ColumnInfo.from_api_response": {"executed_lines": [54, 55, 56, 59, 60, 61, 64, 65, 67], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 46}, "TableInfo._resolve_key": {"executed_lines": [137], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 135}, "TableInfo.__getitem__": {"executed_lines": [140, 141, 142, 143], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 139}, "TableInfo.__contains__": {"executed_lines": [146, 147, 148, 149], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 145}, "TableInfo.__iter__": {"executed_lines": [152], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 151}, "TableInfo.__len__": {"executed_lines": [155], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 154}, "TableInfo.get": {"executed_lines": [159, 160, 161, 162], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 157}, "TableInfo.keys": {"executed_lines": [166], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 164}, "TableInfo.values": {"executed_lines": [170], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 168}, "TableInfo.items": {"executed_lines": [174], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 172}, "TableInfo.from_dict": {"executed_lines": [189], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 179}, "TableInfo.from_api_response": {"executed_lines": [208, 209, 210, 213, 214, 215, 217], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 200}, "TableInfo.to_dict": {"executed_lines": [232], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 230}, "AlternateKeyInfo.from_api_response": {"executed_lines": [262], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 255}, "": {"executed_lines": [6, 8, 9, 11, 14, 15, 36, 37, 38, 39, 40, 41, 42, 43, 45, 46, 79, 80, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 123, 135, 139, 145, 151, 154, 157, 164, 168, 172, 178, 179, 199, 200, 230, 235, 236, 249, 250, 251, 252, 254, 255], "summary": {"covered_lines": 51, "num_statements": 51, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"ColumnInfo": {"executed_lines": [54, 55, 56, 59, 60, 61, 64, 65, 67], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 15}, "TableInfo": {"executed_lines": [137, 140, 141, 142, 143, 146, 147, 148, 149, 152, 155, 159, 160, 161, 162, 166, 170, 174, 189, 208, 209, 210, 213, 214, 215, 217, 232], "summary": {"covered_lines": 27, "num_statements": 27, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 80}, "AlternateKeyInfo": {"executed_lines": [262], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 236}, "": {"executed_lines": [6, 8, 9, 11, 14, 15, 36, 37, 38, 39, 40, 41, 42, 43, 45, 46, 79, 80, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 123, 135, 139, 145, 151, 154, 157, 164, 168, 172, 178, 179, 199, 200, 230, 235, 236, 249, 250, 251, 252, 254, 255], "summary": {"covered_lines": 51, "num_statements": 51, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\models\\upsert.py": {"executed_lines": [6, 8, 9, 11, 14, 15, 38, 39], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [6, 8, 9, 11, 14, 15, 38, 39], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"UpsertItem": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 15}, "": {"executed_lines": [6, 8, 9, 11, 14, 15, 38, 39], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\operations\\__init__.py": {"executed_lines": [11, 13], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [11, 13], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [11, 13], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\operations\\batch.py": {"executed_lines": [6, 8, 10, 12, 13, 14, 35, 36, 37, 42, 47, 64, 75, 76, 78, 101, 103, 116, 118, 128, 131, 149, 150, 151, 153, 154, 156, 157, 165, 177, 178, 180, 197, 199, 219, 221, 244, 246, 274, 276, 316, 317, 318, 319, 320, 321, 322, 323, 325, 326, 329, 351, 352, 354, 383, 393, 402, 404, 413, 415, 436, 438, 450, 452, 465, 467, 484, 486, 500, 502, 509, 511, 520, 522, 558, 578, 589, 590, 592, 610, 611, 612, 620, 643, 644, 646, 665, 666, 667, 668, 670, 672, 673, 674, 675, 679, 681, 716, 717, 718, 719, 720, 721, 723, 724, 725, 726, 730, 732, 733, 738, 740, 741, 742, 743, 744, 745, 747, 749, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782, 790, 820, 821, 822, 823, 824, 825, 826, 827, 829, 844, 845, 846, 848, 866, 867, 870, 886, 887, 889, 895], "summary": {"covered_lines": 150, "num_statements": 151, "percent_covered": 99.33774834437087, "percent_covered_display": "99", "missing_lines": 1, "excluded_lines": 2, "percent_statements_covered": 99.33774834437087, "percent_statements_covered_display": "99"}, "missing_lines": [734], "excluded_lines": [44, 45], "functions": {"ChangeSetRecordOperations.__init__": {"executed_lines": [76], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 75}, "ChangeSetRecordOperations.create": {"executed_lines": [101], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 78}, "ChangeSetRecordOperations.update": {"executed_lines": [116], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 103}, "ChangeSetRecordOperations.delete": {"executed_lines": [128], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 118}, "ChangeSet.__init__": {"executed_lines": [150, 151], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 149}, "ChangeSet.__enter__": {"executed_lines": [154], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 153}, "ChangeSet.__exit__": {"executed_lines": [157], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 156}, "BatchRecordOperations.__init__": {"executed_lines": [178], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 177}, "BatchRecordOperations.create": {"executed_lines": [197], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 180}, "BatchRecordOperations.update": {"executed_lines": [219], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 199}, "BatchRecordOperations.delete": {"executed_lines": [244], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 221}, "BatchRecordOperations.get": {"executed_lines": [274], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 246}, "BatchRecordOperations.upsert": {"executed_lines": [316, 317, 318, 319, 320, 321, 322, 323, 325, 326], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 276}, "BatchTableOperations.__init__": {"executed_lines": [352], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 351}, "BatchTableOperations.create": {"executed_lines": [383], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 354}, "BatchTableOperations.delete": {"executed_lines": [402], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 393}, "BatchTableOperations.get": {"executed_lines": [413], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 404}, "BatchTableOperations.list": {"executed_lines": [436], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 415}, "BatchTableOperations.add_columns": {"executed_lines": [450], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 438}, "BatchTableOperations.remove_columns": {"executed_lines": [465], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 452}, "BatchTableOperations.create_one_to_many_relationship": {"executed_lines": [484], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 467}, "BatchTableOperations.create_many_to_many_relationship": {"executed_lines": [500], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 486}, "BatchTableOperations.delete_relationship": {"executed_lines": [509], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 502}, "BatchTableOperations.get_relationship": {"executed_lines": [520], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 511}, "BatchTableOperations.create_lookup_field": {"executed_lines": [558], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 522}, "BatchQueryOperations.__init__": {"executed_lines": [590], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 589}, "BatchQueryOperations.sql": {"executed_lines": [610, 611, 612], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 592}, "BatchDataFrameOperations.__init__": {"executed_lines": [644], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 643}, "BatchDataFrameOperations.create": {"executed_lines": [665, 666, 667, 668, 670, 672, 673, 674, 675, 679], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 646}, "BatchDataFrameOperations.update": {"executed_lines": [716, 717, 718, 719, 720, 721, 723, 724, 725, 726, 730, 732, 733, 738, 740, 741, 742, 743, 744, 745, 747], "summary": {"covered_lines": 21, "num_statements": 22, "percent_covered": 95.45454545454545, "percent_covered_display": "95", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 95.45454545454545, "percent_statements_covered_display": "95"}, "missing_lines": [734], "excluded_lines": [], "start_line": 681}, "BatchDataFrameOperations.delete": {"executed_lines": [773, 774, 775, 776, 777, 778, 779, 780, 781, 782], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 749}, "BatchRequest.__init__": {"executed_lines": [821, 822, 823, 824, 825, 826, 827], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 820}, "BatchRequest.changeset": {"executed_lines": [844, 845, 846], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 829}, "BatchRequest.execute": {"executed_lines": [866, 867], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 848}, "BatchOperations.__init__": {"executed_lines": [887], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 886}, "BatchOperations.new": {"executed_lines": [895], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 889}, "": {"executed_lines": [6, 8, 10, 12, 13, 14, 35, 36, 37, 42, 47, 64, 75, 78, 103, 118, 131, 149, 153, 156, 165, 177, 180, 199, 221, 246, 276, 329, 351, 354, 393, 404, 415, 438, 452, 467, 486, 502, 511, 522, 578, 589, 592, 620, 643, 646, 681, 749, 790, 820, 829, 848, 870, 886, 889], "summary": {"covered_lines": 55, "num_statements": 55, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [44, 45], "start_line": 1}}, "classes": {"ChangeSetRecordOperations": {"executed_lines": [76, 101, 116, 128], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 64}, "ChangeSet": {"executed_lines": [150, 151, 154, 157], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 131}, "BatchRecordOperations": {"executed_lines": [178, 197, 219, 244, 274, 316, 317, 318, 319, 320, 321, 322, 323, 325, 326], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 165}, "BatchTableOperations": {"executed_lines": [352, 383, 402, 413, 436, 450, 465, 484, 500, 509, 520, 558], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 329}, "BatchQueryOperations": {"executed_lines": [590, 610, 611, 612], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 578}, "BatchDataFrameOperations": {"executed_lines": [644, 665, 666, 667, 668, 670, 672, 673, 674, 675, 679, 716, 717, 718, 719, 720, 721, 723, 724, 725, 726, 730, 732, 733, 738, 740, 741, 742, 743, 744, 745, 747, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782], "summary": {"covered_lines": 42, "num_statements": 43, "percent_covered": 97.67441860465117, "percent_covered_display": "98", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 97.67441860465117, "percent_statements_covered_display": "98"}, "missing_lines": [734], "excluded_lines": [], "start_line": 620}, "BatchRequest": {"executed_lines": [821, 822, 823, 824, 825, 826, 827, 844, 845, 846, 866, 867], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 790}, "BatchOperations": {"executed_lines": [887, 895], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 870}, "": {"executed_lines": [6, 8, 10, 12, 13, 14, 35, 36, 37, 42, 47, 64, 75, 78, 103, 118, 131, 149, 153, 156, 165, 177, 180, 199, 221, 246, 276, 329, 351, 354, 393, 404, 415, 438, 452, 467, 486, 502, 511, 522, 578, 589, 592, 620, 643, 646, 681, 749, 790, 820, 829, 848, 870, 886, 889], "summary": {"covered_lines": 55, "num_statements": 55, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [44, 45], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\operations\\dataframe.py": {"executed_lines": [6, 8, 10, 12, 18, 21, 51, 52, 56, 91, 92, 93, 94, 98, 167, 168, 169, 170, 171, 172, 176, 181, 183, 184, 195, 197, 198, 199, 203, 238, 239, 241, 242, 244, 247, 248, 249, 254, 256, 257, 259, 263, 324, 325, 326, 327, 328, 329, 331, 332, 333, 334, 338, 340, 341, 342, 345, 348, 349, 350, 351, 352, 354, 355, 357, 361, 394, 395, 397, 398, 399, 401, 402, 403, 406, 408, 409, 410, 411], "summary": {"covered_lines": 79, "num_statements": 79, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [14, 15], "functions": {"DataFrameOperations.__init__": {"executed_lines": [52], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 51}, "DataFrameOperations.sql": {"executed_lines": [91, 92, 93, 94], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 56}, "DataFrameOperations.get": {"executed_lines": [167, 168, 169, 170, 171, 172, 176, 181, 183, 184, 195, 197, 198, 199], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 98}, "DataFrameOperations.create": {"executed_lines": [238, 239, 241, 242, 244, 247, 248, 249, 254, 256, 257, 259], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 203}, "DataFrameOperations.update": {"executed_lines": [324, 325, 326, 327, 328, 329, 331, 332, 333, 334, 338, 340, 341, 342, 345, 348, 349, 350, 351, 352, 354, 355, 357], "summary": {"covered_lines": 23, "num_statements": 23, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 263}, "DataFrameOperations.delete": {"executed_lines": [394, 395, 397, 398, 399, 401, 402, 403, 406, 408, 409, 410, 411], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 361}, "": {"executed_lines": [6, 8, 10, 12, 18, 21, 51, 56, 98, 203, 263, 361], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [14, 15], "start_line": 1}}, "classes": {"DataFrameOperations": {"executed_lines": [52, 91, 92, 93, 94, 167, 168, 169, 170, 171, 172, 176, 181, 183, 184, 195, 197, 198, 199, 238, 239, 241, 242, 244, 247, 248, 249, 254, 256, 257, 259, 324, 325, 326, 327, 328, 329, 331, 332, 333, 334, 338, 340, 341, 342, 345, 348, 349, 350, 351, 352, 354, 355, 357, 394, 395, 397, 398, 399, 401, 402, 403, 406, 408, 409, 410, 411], "summary": {"covered_lines": 67, "num_statements": 67, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 21}, "": {"executed_lines": [6, 8, 10, 12, 18, 21, 51, 56, 98, 203, 263, 361], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [14, 15], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\operations\\files.py": {"executed_lines": [6, 8, 14, 17, 35, 36, 40, 104, 105], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [10, 11], "functions": {"FileOperations.__init__": {"executed_lines": [36], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 35}, "FileOperations.upload": {"executed_lines": [104, 105], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 40}, "": {"executed_lines": [6, 8, 14, 17, 35, 40], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [10, 11], "start_line": 1}}, "classes": {"FileOperations": {"executed_lines": [36, 104, 105], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 17}, "": {"executed_lines": [6, 8, 14, 17, 35, 40], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [10, 11], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\operations\\query.py": {"executed_lines": [6, 8, 10, 11, 12, 18, 21, 49, 50, 54, 89, 90, 91, 95, 145, 146, 147, 151, 181, 190, 203, 204, 205, 206, 208, 209, 212, 213, 215, 216, 217, 218, 219, 220, 221, 230, 231, 235, 260, 261, 265, 309, 310, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 330, 331, 332, 333, 334, 335, 336, 337, 339, 349, 350, 354, 391, 392, 393, 394, 395, 400, 401, 402, 403, 411, 436, 437, 441, 478, 479, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 494, 495, 496, 497, 498, 499, 501, 511, 512, 516, 548, 549, 550, 551, 552, 557, 561, 597, 598, 599, 600, 601, 606, 607, 608, 609], "summary": {"covered_lines": 117, "num_statements": 120, "percent_covered": 97.5, "percent_covered_display": "98", "missing_lines": 3, "excluded_lines": 3, "percent_statements_covered": 97.5, "percent_statements_covered_display": "98"}, "missing_lines": [207, 323, 491], "excluded_lines": [14, 15, 445], "functions": {"QueryOperations.__init__": {"executed_lines": [50], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 49}, "QueryOperations.builder": {"executed_lines": [89, 90, 91], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 54}, "QueryOperations.sql": {"executed_lines": [145, 146, 147], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 95}, "QueryOperations.sql_columns": {"executed_lines": [181, 190, 203, 204, 205, 206, 208, 209, 212, 213, 215, 216, 217, 218, 219, 220, 221, 230, 231], "summary": {"covered_lines": 19, "num_statements": 20, "percent_covered": 95.0, "percent_covered_display": "95", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 95.0, "percent_statements_covered_display": "95"}, "missing_lines": [207], "excluded_lines": [], "start_line": 151}, "QueryOperations.sql_select": {"executed_lines": [260, 261], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 235}, "QueryOperations.sql_joins": {"executed_lines": [309, 310, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 330, 331, 332, 333, 334, 335, 336, 337, 339, 349, 350], "summary": {"covered_lines": 24, "num_statements": 25, "percent_covered": 96.0, "percent_covered_display": "96", "missing_lines": 1, "excluded_lines": 0, "percent_statements_covered": 96.0, "percent_statements_covered_display": "96"}, "missing_lines": [323], "excluded_lines": [], "start_line": 265}, "QueryOperations.sql_join": {"executed_lines": [391, 392, 393, 394, 395, 400, 401, 402, 403], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 354}, "QueryOperations.odata_select": {"executed_lines": [436, 437], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 411}, "QueryOperations.odata_expands": {"executed_lines": [478, 479, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 494, 495, 496, 497, 498, 499, 501, 511, 512], "summary": {"covered_lines": 21, "num_statements": 22, "percent_covered": 95.45454545454545, "percent_covered_display": "95", "missing_lines": 1, "excluded_lines": 1, "percent_statements_covered": 95.45454545454545, "percent_statements_covered_display": "95"}, "missing_lines": [491], "excluded_lines": [445], "start_line": 441}, "QueryOperations.odata_expand": {"executed_lines": [548, 549, 550, 551, 552, 557], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 516}, "QueryOperations.odata_bind": {"executed_lines": [597, 598, 599, 600, 601, 606, 607, 608, 609], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 561}, "": {"executed_lines": [6, 8, 10, 11, 12, 18, 21, 49, 54, 95, 151, 235, 265, 354, 411, 441, 516, 561], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [14, 15], "start_line": 1}}, "classes": {"QueryOperations": {"executed_lines": [50, 89, 90, 91, 145, 146, 147, 181, 190, 203, 204, 205, 206, 208, 209, 212, 213, 215, 216, 217, 218, 219, 220, 221, 230, 231, 260, 261, 309, 310, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 330, 331, 332, 333, 334, 335, 336, 337, 339, 349, 350, 391, 392, 393, 394, 395, 400, 401, 402, 403, 436, 437, 478, 479, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 494, 495, 496, 497, 498, 499, 501, 511, 512, 548, 549, 550, 551, 552, 557, 597, 598, 599, 600, 601, 606, 607, 608, 609], "summary": {"covered_lines": 99, "num_statements": 102, "percent_covered": 97.05882352941177, "percent_covered_display": "97", "missing_lines": 3, "excluded_lines": 1, "percent_statements_covered": 97.05882352941177, "percent_statements_covered_display": "97"}, "missing_lines": [207, 323, 491], "excluded_lines": [445], "start_line": 21}, "": {"executed_lines": [6, 8, 10, 11, 12, 18, 21, 49, 54, 95, 151, 235, 265, 354, 411, 441, 516, 561], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [14, 15], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\operations\\records.py": {"executed_lines": [6, 8, 10, 11, 17, 20, 46, 47, 57, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 110, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 174, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 228, 229, 262, 263, 331, 426, 427, 428, 429, 438, 443, 444, 445, 447, 448, 449, 460, 462, 466, 542, 543, 544, 545, 546, 547, 548, 549, 551, 552, 553, 554, 555, 556, 558, 559, 560, 561], "summary": {"covered_lines": 86, "num_statements": 86, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 18, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [13, 14, 51, 52, 53, 54, 55, 56, 168, 169, 170, 171, 172, 173, 260, 261, 329, 330], "functions": {"RecordOperations.__init__": {"executed_lines": [47], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 46}, "RecordOperations.create": {"executed_lines": [94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 57}, "RecordOperations.update": {"executed_lines": [155, 156, 157, 158, 159, 160, 161, 162, 163, 164], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 110}, "RecordOperations.delete": {"executed_lines": [210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 174}, "RecordOperations.get": {"executed_lines": [426, 427, 428, 429, 438, 443, 444, 445, 447, 462], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 331}, "RecordOperations.get._paged": {"executed_lines": [448, 449, 460], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 447}, "RecordOperations.upsert": {"executed_lines": [542, 543, 544, 545, 546, 547, 548, 549, 551, 552, 553, 554, 555, 556, 558, 559, 560, 561], "summary": {"covered_lines": 18, "num_statements": 18, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 466}, "": {"executed_lines": [6, 8, 10, 11, 17, 20, 46, 57, 110, 174, 228, 229, 262, 263, 331, 466], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 12, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [13, 14, 51, 53, 54, 56, 168, 170, 171, 173, 261, 330], "start_line": 1}}, "classes": {"RecordOperations": {"executed_lines": [47, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 426, 427, 428, 429, 438, 443, 444, 445, 447, 448, 449, 460, 462, 542, 543, 544, 545, 546, 547, 548, 549, 551, 552, 553, 554, 555, 556, 558, 559, 560, 561], "summary": {"covered_lines": 70, "num_statements": 70, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 6, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [52, 55, 169, 172, 260, 329], "start_line": 20}, "": {"executed_lines": [6, 8, 10, 11, 17, 20, 46, 57, 110, 174, 228, 229, 262, 263, 331, 466], "summary": {"covered_lines": 16, "num_statements": 16, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 12, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [13, 14, 51, 53, 54, 56, 168, 170, 171, 173, 261, 330], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\operations\\tables.py": {"executed_lines": [6, 8, 10, 17, 18, 19, 20, 26, 29, 65, 66, 70, 135, 136, 143, 147, 164, 165, 169, 189, 190, 191, 192, 193, 197, 245, 246, 250, 278, 279, 283, 311, 312, 316, 380, 381, 386, 396, 436, 437, 441, 450, 469, 470, 474, 493, 494, 495, 496, 497, 501, 567, 568, 579, 583, 634, 635, 636, 637, 646, 668, 669, 670, 674, 698, 699, 703, 749, 750, 754, 793, 794, 798, 838, 839], "summary": {"covered_lines": 75, "num_statements": 75, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [22, 23], "functions": {"TableOperations.__init__": {"executed_lines": [66], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 65}, "TableOperations.create": {"executed_lines": [135, 136, 143], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 70}, "TableOperations.delete": {"executed_lines": [164, 165], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 147}, "TableOperations.get": {"executed_lines": [189, 190, 191, 192, 193], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 169}, "TableOperations.list": {"executed_lines": [245, 246], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 197}, "TableOperations.add_columns": {"executed_lines": [278, 279], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 250}, "TableOperations.remove_columns": {"executed_lines": [311, 312], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 283}, "TableOperations.create_one_to_many_relationship": {"executed_lines": [380, 381, 386], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 316}, "TableOperations.create_many_to_many_relationship": {"executed_lines": [436, 437, 441], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 396}, "TableOperations.delete_relationship": {"executed_lines": [469, 470], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 450}, "TableOperations.get_relationship": {"executed_lines": [493, 494, 495, 496, 497], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 474}, "TableOperations.create_lookup_field": {"executed_lines": [567, 568, 579], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 501}, "TableOperations.create_alternate_key": {"executed_lines": [634, 635, 636, 637], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 583}, "TableOperations.get_alternate_keys": {"executed_lines": [668, 669, 670], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 646}, "TableOperations.delete_alternate_key": {"executed_lines": [698, 699], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 674}, "TableOperations.list_columns": {"executed_lines": [749, 750], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 703}, "TableOperations.list_relationships": {"executed_lines": [793, 794], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 754}, "TableOperations.list_table_relationships": {"executed_lines": [838, 839], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 798}, "": {"executed_lines": [6, 8, 10, 17, 18, 19, 20, 26, 29, 65, 70, 147, 169, 197, 250, 283, 316, 396, 450, 474, 501, 583, 646, 674, 703, 754, 798], "summary": {"covered_lines": 27, "num_statements": 27, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [22, 23], "start_line": 1}}, "classes": {"TableOperations": {"executed_lines": [66, 135, 136, 143, 164, 165, 189, 190, 191, 192, 193, 245, 246, 278, 279, 311, 312, 380, 381, 386, 436, 437, 441, 469, 470, 493, 494, 495, 496, 497, 567, 568, 579, 634, 635, 636, 637, 668, 669, 670, 698, 699, 749, 750, 793, 794, 838, 839], "summary": {"covered_lines": 48, "num_statements": 48, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 29}, "": {"executed_lines": [6, 8, 10, 17, 18, 19, 20, 26, 29, 65, 70, 147, 169, 197, 250, 283, 316, 396, 450, 474, 501, 583, 646, 674, 703, 754, 798], "summary": {"covered_lines": 27, "num_statements": 27, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 2, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [22, 23], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\utils\\__init__.py": {"executed_lines": [10], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [10], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [10], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\Dataverse\\utils\\_pandas.py": {"executed_lines": [6, 8, 9, 11, 12, 15, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 36, 43, 44, 45, 46, 47, 48, 49, 50, 51, 55, 56, 58, 59, 60], "summary": {"covered_lines": 34, "num_statements": 34, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"_normalize_scalar": {"executed_lines": [21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33], "summary": {"covered_lines": 13, "num_statements": 13, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 15}, "dataframe_to_records": {"executed_lines": [43, 44, 45, 46, 47, 48, 49, 50, 51, 55, 56, 58, 59, 60], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 36}, "": {"executed_lines": [6, 8, 9, 11, 12, 15, 36], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [6, 8, 9, 11, 12, 15, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 36, 43, 44, 45, 46, 47, 48, 49, 50, 51, 55, 56, 58, 59, 60], "summary": {"covered_lines": 34, "num_statements": 34, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}, "src\\PowerPlatform\\__init__.py": {"executed_lines": [4], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "functions": {"": {"executed_lines": [4], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}, "classes": {"": {"executed_lines": [4], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0, "percent_statements_covered": 100.0, "percent_statements_covered_display": "100"}, "missing_lines": [], "excluded_lines": [], "start_line": 1}}}}, "totals": {"covered_lines": 3858, "num_statements": 4956, "percent_covered": 77.84503631961259, "percent_covered_display": "78", "missing_lines": 1098, "excluded_lines": 71, "percent_statements_covered": 77.84503631961259, "percent_statements_covered_display": "78"}} \ No newline at end of file diff --git a/docs/async-design-options.md b/docs/async-design-options.md deleted file mode 100644 index cb4a2908..00000000 --- a/docs/async-design-options.md +++ /dev/null @@ -1,288 +0,0 @@ -# Async SDK — Implementation Design Options - -**Status:** Decision pending -**Date:** April 2026 - ---- - -## Background - -Adding async support touches every layer of the SDK. The HTTP transport, authentication, data layer, operations, and public client entry point all require async versions. The query builder needs only minimal changes (the fluent chain stays sync; only `execute()` becomes a coroutine). The models layer (`Record`, `TableInfo`, filters, etc.) requires **no changes** — pure dataclasses are shared between sync and async as-is. The existing sync client and all sync behaviour are **untouched**. See Part 3 for a full layer-by-layer breakdown. - -Two decisions are open. Everything else — HTTP transport, auth, operations, query builder — follows directly from these two choices and has no meaningful alternatives. - -1. **How should the async data layer relate to the sync data layer?** The data layer contains a mix of pure logic (payload building, parsing, validation) and I/O calls. The question is whether the async client *inherits* from the sync client and overrides I/O methods, or whether pure logic is *extracted into a shared base* that both sync and async inherit from as siblings. See Part 1. - -2. **Where should async files live in the package tree?** Either all async files go under a dedicated `aio/` sub-package (the Azure SDK convention), or they are placed alongside their sync counterparts in the existing folders. See Part 2. - ---- - -## Part 1 — Implementation Design - -Two options were explored for how the async data layer relates to the sync data layer. - ---- - -### Option A — Async inherits from Sync - -The async client is a subclass of the sync client and overrides every I/O method with an `async def`. - -``` -_ODataClient - └── _AsyncODataClient (overrides all I/O methods) - -_BatchClient - └── _AsyncBatchClient (overrides all I/O methods) -``` - -**Pros** - -- Fewer files — no extra base classes needed. -- DRY without indirection — shared logic lives once in the sync class. -- Sync tests implicitly cover the shared methods. -- Simple inheritance chain, easy to read top-down. - -**Cons** - -- **LSP violation.** The Liskov Substitution Principle (LSP) states that a subclass must be usable wherever its parent class is expected, without breaking the program. Here, `_AsyncODataClient` is a subclass of `_ODataClient`, yet every I/O method changes from a regular function to a coroutine (`async def`). A caller written against `_ODataClient` expects `client.get(...)` to return a result directly; the async subclass returns a coroutine object instead, which is a different type entirely. -- Every overridden method requires `# type: ignore[override]` to silence the type checker (44 suppressions in this codebase). -- Conceptually misleading — "is a" implies substitutability; async-over-sync does not have it. -- Sync and async surfaces must evolve in lockstep or the inheritance chain silently inherits wrong sync behavior. - ---- - -### Option B — Shared Pure Base + Sibling Sync/Async - -Pure (I/O-free) methods are extracted into a base class. The sync and async clients both inherit from that base and are siblings. - -``` -_ODataBase (pure methods: validation, payload building, parsing) - ├── _ODataClient (sync I/O) - └── _AsyncODataClient (async I/O) - -_BatchBase (pure methods: multipart serialisation, response parsing) - ├── _BatchClient (sync I/O) - └── _AsyncBatchClient (async I/O) -``` - -**Pros** - -- Correct OOP — the relationship is "shares pure logic", not "is a". No LSP violation: because async and sync clients are siblings rather than parent/child, neither can be passed where the other is expected, so the type system enforces the boundary automatically. -- Zero `# type: ignore` suppressions — async methods are first-class definitions, not overrides. -- Type-safe out of the box; type checkers validate rather than suppress. -- Matches the Azure SDK pattern (azure-storage-blob, azure-data-tables, etc.). -- Base classes are independently testable. - -**Cons** - -- **Requires refactoring the existing sync layer.** Pure methods must be extracted out of `_ODataClient` and `_BatchClient` and moved into the new base classes. This is a non-trivial change to production code that was not touched by Option A, introducing risk and review surface for the sync path even though its behavior is unchanged. -- Two extra files (`_odata_base.py`, `_batch_base.py`). -- Slightly more indirection — reading the full API surface requires checking both the base and the subclass. -- The pure/IO boundary must be actively maintained as new methods are added. - ---- - -## Part 2 — Folder Structure - -Two options were explored for where async files live in the package tree. - ---- - -### Option A — Dedicated `aio/` folder - -All async code lives under a separate `aio/` sub-package, mirroring the sync layout. - -``` -src/PowerPlatform/Dataverse/ -├── core/ # sync -│ ├── _auth.py -│ └── _http.py -├── data/ # sync data layer -│ ├── _odata.py -│ ├── _odata_base.py # (if using Implementation Option B) -│ ├── _batch.py -│ └── _batch_base.py -├── operations/ # sync public operations (records, tables, …) -└── aio/ # ALL async code - ├── core/ # async counterparts to core/ - │ ├── _async_auth.py - │ └── _async_http.py - ├── data/ # async data layer - │ ├── _async_odata.py - │ └── _async_batch.py - └── operations/ # async public operations -``` - -**Pros** - -- `aio/` is the well-established Python convention (`aiohttp`, `azure-storage-blob`, `motor`, etc.). -- All async code is discoverable from a single entry point. -- Enforces a hard boundary — async and sync cannot accidentally be mixed at import time. -- Users who only want the sync client never need to open the `aio/` tree. - -**Cons** - -- Related sync/async files are in different directories — comparing or maintaining parity requires navigating across the tree. -- Slightly deeper nesting. - ---- - -### Option B — Co-located in existing folders - -Async files live alongside their sync counterparts, distinguished by a naming prefix/suffix. - -``` -src/PowerPlatform/Dataverse/ -├── core/ -│ ├── _auth.py -│ ├── _async_auth.py # next to _auth.py -│ ├── _http.py -│ └── _async_http.py # next to _http.py -├── data/ -│ ├── _odata.py -│ ├── _async_odata.py # next to _odata.py -│ ├── _odata_base.py -│ ├── _batch.py -│ ├── _async_batch.py -│ └── _batch_base.py -└── operations/ - ├── records.py - ├── _async_records.py # next to records.py - └── … -``` - -**Pros** - -- Sync and async counterparts sit side-by-side — parity gaps are immediately visible. -- Flatter structure, fewer directories. -- No sub-package boundary to cross when reading related files. - -**Cons** - -- Does not follow the `aio/` convention expected by most Python developers. -- Each folder mixes sync and async concerns; harder to see the async surface at a glance. -- **Async deps become mandatory for all sync users.** A proper public async API requires exporting async classes from `__init__.py`. Because that file is on the import path for every user, sync-only users who have not installed the async dependency will get a `ModuleNotFoundError` at `from PowerPlatform.Dataverse import DataverseClient` — not at the point of async usage. The only escape is to require users to import from private module paths, which is not a viable public API. Verified empirically: adding one async export to `operations/__init__.py` caused the sync client import to fail when the async dep was absent. -- No package-level boundary to prevent accidental cross-imports. - ---- - -## Appendix — Full SDK Structure Under Each Folder Option - -The trees below show the complete package layout. Async files are marked with `*`. -The sync-only layers (`models/`, `common/`, `core/errors`, `utils/`) are identical in both options and included for completeness. - -### Folder Option A — Dedicated `aio/` sub-package - -``` -src/PowerPlatform/Dataverse/ -├── client.py # DataverseClient (sync entry point) -├── common/ -│ └── constants.py -├── core/ -│ ├── _auth.py -│ ├── _http.py -│ ├── _http_logger.py -│ ├── config.py -│ ├── errors.py -│ └── log_config.py -├── data/ -│ ├── _odata.py -│ ├── _odata_base.py # shared pure base (if Implementation Option B) -│ ├── _batch.py -│ ├── _batch_base.py # shared pure base (if Implementation Option B) -│ ├── _relationships.py -│ ├── _upload.py -│ └── _raw_request.py -├── models/ -│ ├── batch.py -│ ├── filters.py -│ ├── labels.py -│ ├── query_builder.py -│ ├── record.py -│ ├── relationship.py -│ ├── table_info.py -│ └── upsert.py -├── operations/ -│ ├── batch.py -│ ├── dataframe.py -│ ├── files.py -│ ├── query.py -│ ├── records.py -│ └── tables.py -├── utils/ -│ └── _pandas.py -└── aio/ # * all async code lives here - ├── async_client.py # * AsyncDataverseClient (async entry point) - ├── core/ - │ ├── _async_auth.py # * AsyncTokenCredential impl - │ └── _async_http.py # * aiohttp-based HTTP client - ├── data/ - │ ├── _async_odata.py # * async OData client - │ ├── _async_batch.py # * async batch client - │ ├── _async_relationships.py # * async relationships mixin - │ └── _async_upload.py # * async file upload mixin - └── operations/ - ├── async_batch.py # * async batch operations - ├── async_dataframe.py # * async dataframe operations - ├── async_files.py # * async file operations - ├── async_query.py # * async query builder - ├── async_records.py # * async record operations - └── async_tables.py # * async table operations -``` - ---- - -### Folder Option B — Co-located async files - -``` -src/PowerPlatform/Dataverse/ -├── client.py # DataverseClient (sync entry point) -├── async_client.py # * AsyncDataverseClient (async entry point) -├── common/ -│ └── constants.py -├── core/ -│ ├── _auth.py -│ ├── _async_auth.py # * AsyncTokenCredential impl -│ ├── _http.py -│ ├── _async_http.py # * aiohttp-based HTTP client -│ ├── _http_logger.py -│ ├── config.py -│ ├── errors.py -│ └── log_config.py -├── data/ -│ ├── _odata.py -│ ├── _async_odata.py # * async OData client -│ ├── _odata_base.py # shared pure base (if Implementation Option B) -│ ├── _batch.py -│ ├── _async_batch.py # * async batch client -│ ├── _batch_base.py # shared pure base (if Implementation Option B) -│ ├── _relationships.py -│ ├── _async_relationships.py # * async relationships mixin -│ ├── _upload.py -│ ├── _async_upload.py # * async file upload mixin -│ └── _raw_request.py -├── models/ -│ ├── batch.py -│ ├── filters.py -│ ├── labels.py -│ ├── query_builder.py -│ ├── record.py -│ ├── relationship.py -│ ├── table_info.py -│ └── upsert.py -├── operations/ -│ ├── batch.py -│ ├── async_batch.py # * async batch operations -│ ├── dataframe.py -│ ├── async_dataframe.py # * async dataframe operations -│ ├── files.py -│ ├── async_files.py # * async file operations -│ ├── query.py -│ ├── async_query.py # * async query builder -│ ├── records.py -│ ├── async_records.py # * async record operations -│ ├── tables.py -│ └── async_tables.py # * async table operations -└── utils/ - └── _pandas.py -``` diff --git a/docs/async-design.md b/docs/async-design.md deleted file mode 100644 index 55a2c3b7..00000000 --- a/docs/async-design.md +++ /dev/null @@ -1,276 +0,0 @@ -# Async SDK Design - -| | | -|---|---| -| **Status** | In progress | -| **Date** | April 2026 | -| **Options analysis** | [async-design-options.md](./async-design-options.md) | - ---- - -## Summary - -- `AsyncDataverseClient` will be a new standalone client that mirrors the sync `DataverseClient` API — the sync client and all its behavior are untouched -- Pure logic (payload building, URL construction, parsing) will live in shared base classes (`_ODataBase`, `_BatchBase`) inherited by both sync and async clients independently, not through inheritance of one from the other -- All async code will live under a dedicated **`aio/` sub-package** — async dependencies will be fully isolated and will never affect sync-only users -- `aiohttp` will be an **optional dependency**; sync-only users will install nothing new -- Full design options analysis: [async-design-options.md](./async-design-options.md) - -**What's unchanged:** Models, error classes, configuration, constants, and utilities require no async changes and will be reused as-is by both clients. - ---- - -## Implementation Pattern — Shared Pure Base + Sibling Clients - -Two patterns were considered: - -- **Inheritance pattern** — the async client inherits from the sync client and overrides all I/O methods with async equivalents -- **Sibling pattern** — pure logic is extracted into a shared base; the sync and async clients both inherit from that base independently - -The sibling pattern is proposed. See [async-design-options.md](./async-design-options.md) for the full comparison. - -In the sibling pattern, pure logic will live in `_ODataBase` and `_BatchBase`. The sync and async clients will inherit from the same base and will be siblings. - -``` -_ODataBase (pure: URL building, payload construction, parsing, caches) - ├── _ODataClient (sync I/O) - └── _AsyncODataClient (async I/O) - -_BatchBase (pure: multipart serialisation, response parsing) - ├── _BatchClient (sync I/O) - └── _AsyncBatchClient (async I/O) -``` - -**Why the sibling pattern:** - -- **Correct relationship** — async is not a subtype of sync. The sibling pattern has both clients inherit from a pure base class, a valid is-a relationship that both satisfy. -- **No silent coupling** — in the inheritance pattern, sync changes (a new I/O method, or an existing method gaining I/O) are silently inherited by the async client, potentially blocking the event loop with no error. In the sibling pattern, a missing async method fails immediately. -- **Type safety** — no `# type: ignore[override]` suppressions needed. Async methods are first-class definitions, not overrides with mismatched return types. - -**Tradeoff accepted:** This pattern requires extracting pure logic out of the sync clients. - ---- - -### Folder Structure — Dedicated Async Sub-package - -Two patterns were considered: - -- **Co-location pattern** — async files placed alongside their sync counterparts in the existing folders, distinguished by a naming prefix -- **Dedicated async sub-package pattern** — all async code grouped under a dedicated sub-package, mirroring the sync layout - -The dedicated async sub-package pattern is proposed. See [async-design-options.md](./async-design-options.md) for the full comparison. - -All async code will live under `aio/`, mirroring the sync layout. - -``` -src/PowerPlatform/Dataverse/ -├── client.py # DataverseClient (sync entry point) -├── data/ -│ ├── _odata_base.py # shared pure base -│ ├── _odata.py # sync OData client -│ ├── _batch_base.py # shared pure base -│ └── _batch.py # sync batch client -├── operations/ # sync operations -│ ├── records.py -│ ├── tables.py -│ ├── query.py -│ ├── batch.py -│ ├── dataframe.py -│ └── files.py -└── aio/ # ALL async code - ├── async_client.py # AsyncDataverseClient (async entry point) - ├── core/ - │ ├── _async_auth.py # AsyncTokenCredential implementation - │ └── _async_http.py # aiohttp-based HTTP client - ├── data/ - │ ├── _async_odata.py # async OData client - │ └── _async_batch.py # async batch client - └── operations/ - ├── async_records.py - ├── async_tables.py - ├── async_query.py - ├── async_batch.py - ├── async_dataframe.py - └── async_files.py -``` - -**Why the dedicated async sub-package:** - -- **Dependency isolation** — the `aio/` boundary ensures `aiohttp` is never imported by the sync path. With co-location, `__init__.py` eager imports or accidental cross-imports can pull async deps into the sync path, causing `ImportError` for users who never installed `aiohttp`. -- **Azure SDK convention** — `azure-storage-blob`, `azure-data-tables`, and other Azure SDKs expose async clients under `aio/` sub-package ([Python Guidelines](https://azure.github.io/azure-sdk/python_design.html)), lowering the learning curve for developers who use other Azure SDKs. -- **Discoverability** — the full async surface is visible in one directory tree, not scattered across every folder in the project. - -**Tradeoff accepted:** Sync and async counterparts live in different directories. - ---- - -## SDK Components - -| Component | Existing files | Async change | -|---|---|---| -| Entry point | `client.py` | New `AsyncDataverseClient` entry point | -| Core | `_auth.py`, `_http.py`, `_http_logger.py`, `config.py`, `errors.py`, `log_config.py` | New async auth (`AsyncTokenCredential`) and async HTTP client (`aiohttp`); rest reused as-is | -| Data layer | `_odata.py`, `_batch.py`, `_relationships.py`, `_upload.py`, `_raw_request.py` | New async OData, batch, relationships, and upload inheriting shared pure bases; `_raw_request.py` reused as-is | -| Operations | `records.py`, `tables.py`, `query.py`, `batch.py`, `dataframe.py`, `files.py` | New async counterpart for each — thin `async def` + `await` delegation wrappers | -| Models | `query_builder.py`, `record.py`, `filters.py`, `batch.py`, `relationship.py`, `table_info.py`, `upsert.py` | All reused as-is; `execute()` and `to_dataframe()` in query builder become coroutines in the async path | -| Common | `constants.py` | Reused as-is | -| Utils | `_pandas.py` | Reused as-is | - ---- - -## Dependencies - -| Dependency | Type | Required by | -|---|---|---| -| `aiohttp>=3.9` | Optional runtime | `aio/` only — never imported by the sync path | -| `pytest-asyncio` | test | Async test suite | - -`aiohttp` will be listed as an optional extra in `pyproject.toml`. Sync-only users who do not install the extra will never encounter an import error originating from the async path. - ---- - -## Implementation Notes - -### `ClientSession` Lifecycle - -`aiohttp.ClientSession` requires explicit closure to drain in-flight requests and release connections. Both usage patterns are supported: - -```python -# Context manager (preferred — session lifecycle is explicit) -async with AsyncDataverseClient(url, credential) as client: - await client.records.get(...) - -# Standalone (supported — caller is responsible for closing) -client = AsyncDataverseClient(url, credential) -try: - await client.records.get(...) -finally: - await client.close() -``` - -One `ClientSession` will be shared across all requests for the client's lifetime. Creating a new session per request defeats connection pooling and is an antipattern in `aiohttp`. The session is created lazily on the first request for standalone usage, or in `__aenter__` for context manager usage, and passed down to `_AsyncODataClient` → `_AsyncHttpClient`, which uses it but does not own it. - -**Timeouts:** Timeouts will be configured per-request via `aiohttp.ClientTimeout`, matching the sync client's per-method defaults (120s for writes, 10s for reads). `aiohttp` automatically discards failed or timed-out connections from the pool — no manual pool recovery is needed. - ---- - -### Concurrency — `asyncio.gather` + `Semaphore` - -For bulk record operations (`records.create()`, `records.update()`, `records.upsert()`), the sync SDK uses `ThreadPoolExecutor(max_workers=N)` to dispatch record chunks concurrently. The async equivalent will replace the thread pool with `asyncio.gather()` and an `asyncio.Semaphore` to enforce a concurrency cap (`max_workers` defaults to `10`; async coroutines are cheap so the cap is driven by Dataverse server-side throttling limits, not client resource constraints). - -```python -semaphore = asyncio.Semaphore(max_workers) - -async def _bounded(chunk): - async with semaphore: - return await _execute_with_retry_async(chunk) - -await asyncio.gather(*[_bounded(chunk) for chunk in chunks]) -``` - -**429 throttling:** Transient errors (429, 503, 504) will trigger a retry with backoff. No new chunk will be dispatched while one is waiting out its backoff — the concurrency cap is maintained during retries, matching the sync throttling behavior. - ---- - -### QueryBuilder - -`QueryBuilder.execute()` and `QueryBuilder.to_dataframe()` are sync methods that call into the sync client. In the async path, `AsyncQueryOperations.builder()` returns an `AsyncQueryBuilder` subclass that overrides both as `async def`, delegating to the async client. - -**At GA**, `QueryBuilder` will be replaced by an inert `SelectQuery` builder, with `build()` remaining on `SelectQuery` and execution moving to `QueryOperations.execute()` / `AsyncQueryOperations.execute()`. `SelectQuery` is shared between sync and async — no async variant is needed. The async path adds only `await`: - -```python -result = await client.query.execute( - select("name", "revenue") - .from_("account") - .where(eq("statecode", 0)) -) -for record in result: - print(record["name"]) -``` - ---- - -### Error Handling and Cancellation - -**`asyncio.CancelledError`** propagates — the SDK never suppresses it. Since Python 3.8 it is a `BaseException`, so the retry loop's `except aiohttp.ClientError` clause will not catch it. A cancelled request's connection is discarded from the pool; the `ClientSession` remains valid for future requests. Final cleanup is handled by `async with` or `await client.close()`. - -**Error types:** No new async-specific error types are introduced. - -| Failure type | Sync | Async | -|---|---|---| -| HTTP error (4xx, 5xx) | `HttpError` | `HttpError` — reused as-is | -| Network error (connection, timeout) | `requests.exceptions.RequestException` | `aiohttp.ClientError` — same pattern, different library | -| Cancellation | N/A | `asyncio.CancelledError` — propagates as `BaseException` | - -`HttpError`, `ValidationError`, `MetadataError`, and `SQLParseError` are all reused unchanged. - ---- - -### Pagination - -OData pagination is sequential — each `@odata.nextLink` URL is only known after receiving the previous page's response. The async implementation will use an async generator, structurally identical to the sync version with `await` on each page fetch. - -The SDK has two pagination patterns, each consumed differently: - -- **OData (`records.get`)** — async generator; iterated with `async for`, yields one page at a time. Each `@odata.nextLink` is fetched sequentially. - - ```python - async with AsyncDataverseClient(url, credential) as client: - async for page in client.records.get( - "account", - filter="statecode eq 0", - select=["name", "telephone1"], - page_size=50, - ): - for record in page: - print(record["name"]) - ``` - -- **SQL (`query.sql`)** — coroutine; called with `await`, returns a flat list after all pages are collected internally. - - ```python - rows = await client.query.sql("SELECT TOP 100 name FROM account") - for row in rows: - print(row["name"]) - ``` - ---- - -## Testing Strategy - -- Unit tests for all async implementations with >= 90% coverage -- Integration tests similar to the existing sync integration tests — same scenarios and coverage, adapted to use `AsyncDataverseClient` with `async`/`await` - ---- - -## Implementation Phases - -| Phase | Deliverable | -|---|---| -| 1 — Refactoring | Extract pure logic into `_ODataBase` and `_BatchBase`; sync client inherits from base; all existing sync tests should pass | -| 2 — Async implementation | `aio/` sub-package with async HTTP, auth, data layer, operations, and `AsyncDataverseClient` entry point | - ---- - -## Examples - -### Basic usage - -```python -from PowerPlatform.Dataverse.aio import AsyncDataverseClient - -async with AsyncDataverseClient(url, credential) as client: - record_id = await client.records.create("account", {"name": "Contoso"}) - record = await client.records.get("account", record_id) - await client.records.delete("account", record_id) -``` - -### Batch - -```python -async with AsyncDataverseClient(url, credential) as client: - batch = client.batch.new() - batch.records.create("account", {"name": "A"}) - batch.records.create("account", {"name": "B"}) - result = await batch.execute() -``` diff --git a/docs/design-evaluation.md b/docs/design-evaluation.md deleted file mode 100644 index dbdca80d..00000000 --- a/docs/design-evaluation.md +++ /dev/null @@ -1,344 +0,0 @@ -# Dataverse Python SDKs — Design Evaluation - -**Status**: Draft for review - ---- - -## 1. Summary - -Two Python SDKs for Dataverse are in flight: a **client SDK** for scripts, notebooks, and integrations (v0.1.0b8, approaching GA) and a **server-side runtime** for Python plugins running in the Dataverse sandbox (greenfield, in design). Both face the same typing question — stay with the current dict-based API (`client.records.create("account", {"name": "X"})`) or adopt strongly-typed entity classes (`client.records.create(Account(name="X"))`) — and either SDK could plausibly answer differently from the other. - -This document recommends an **asymmetric** answer: - -- **Client SDK at GA — dict API + SQL-style query surface.** The dict write API (`client.records.create("account", {...})`) ships as the GA contract. The beta's fluent `QueryBuilder` is **replaced pre-GA** by a SQL-style query surface (`select(...).where(...)`) matching the server SDK — one query idiom across script and plugin code. GA timeline adjusts to accommodate; the permanent cost of shipping two query surfaces outweighs the schedule delta. -- **Client SDK typing — evidence-gated.** Whether to *add* a strongly-typed entity layer on top of the dict API is deferred to a head-to-head evaluation (§3.1), not shipped on theoretical benefit. A `DataverseModel` Protocol ships pre-GA as a ~10-line seam so the option stays open without a future API break. -- **Server SDK — ship typed.** The production-reliability case (§3.2) justifies typing independent of agent-authoring behavior. -- **Both SDKs share the same query idiom and shared model package.** SQL-style (`select` / `insert_into` / `update` / `delete`) is the single query surface. `Entity`, `FilterExpression`, `Record`, and the SQL-style free functions live in one shared package consumed by both SDKs. - -### 1.1 Decisions at a glance - -| Question | Decision | -|---|---| -| Delay GA for **typing** work? | No — typing decision is evidence-gated (§3.1) | -| Delay GA for the **SQL-style query-surface** replacement? | Yes — accept a schedule shift; the permanent two-surface tax outweighs the delta (§3.3) | -| One pre-GA change worth making (beyond the query surface)? | Yes — define `DataverseModel` Protocol and widen write-signatures to accept it (~10 lines, zero behavioral change) | -| Ship a typed entity layer in the client SDK? | Evaluation-gated — see §3.1 | -| Ship a typed entity layer in the server SDK? | Yes — see §3.2 | -| Require code generation to use either SDK? | No. Codegen is always opt-in, build-time, scoped via `--tables` | -| Shared `Entity` / query IR across client and server? | Yes — one package, one generator, one output format | -| Primary query idiom in both SDKs? | SQL-style (`select` / `insert_into` / `update` / `delete`) — **sole** programmatic query builder at GA | -| Keep fluent `QueryBuilder` from beta at GA? | No. One query idiom, not two. Beta callers migrate via codemod | -| Runtime metadata fetching at client startup? | No — defeats static analysis and introduces cold-start tax | -| Deprecate the dict API ever? | No | - -### 1.2 Phased plan - -1. **Client SDK GA** — ship the dict API. Replace the beta's fluent `QueryBuilder` with SQL-style (`select` / `insert_into` / `update` / `delete`) as the sole programmatic query builder. Add the `DataverseModel` Protocol seam (§4). Publish a codemod to migrate beta users. -2. **Evaluation (1–2 weeks)** — run 20 representative tasks across three API arms; decide whether typed client-side adds measurable value (§3.1). -3. **Client SDK post-GA minor** — path branches on the evaluation. Either stop at GA's dict + SQL-style surface, or additionally ship the typed entity layer. -4. **Server SDK alpha** — typed-first, SQL-style, shared query IR with client. Independent of client evaluation result. - ---- - -## 2. The three approaches - -### 2.1 Dict-based (current shipping client SDK) - -```python -client.records.create("account", {"name": "Contoso", "revenue": 1_000_000}) -row = client.records.get("account", guid) -row["name"] # string keys; no IDE autocomplete; typos surface at runtime -``` - -Pros: zero ceremony, excellent for scripts and notebooks, trivially Pythonic. -Cons: no compile-time validation, no autocomplete. - -### 2.2 Strongly-typed with mandatory codegen - -Two sub-variants; both are rejected. - -**Runtime generation** (types built from a live metadata fetch at startup): - -```python -Account = await client.get_type("account") # network round-trip required -client.records.create(Account(name="Contoso")) -``` - -Failure modes: -- Static analyzers (mypy / pyright / Pylance) cannot see types built by `type(...)` at runtime — they resolve to `Any`. The headline benefit of typing does not arrive. -- Per-cold-invocation metadata fetch in serverless environments. -- Schema drift between runs is silent and non-reviewable. -- Offline testing requires a metadata mock or a live org. - -**Build-time with hard cutover** (generator emits `.py` files; dict API removed): -- A breaking change for existing users with no benefit over the additive approach below. - -### 2.3 Additive typed layer (recommended shape if typing ships) - -```python -# Dict form continues to work unchanged -client.records.create("account", {"name": "Contoso"}) - -# Typed form is opt-in -class Account(Entity, table="account", primary_key="accountid"): - name = Text(max_length=160) - revenue = Money() - -client.records.create(Account(name="Contoso", revenue=1_000_000)) -``` - -- All operations accept `Union[str, type[Entity]]` and `Union[dict, Entity]`. -- Code generation is build-time, produces editable Python files checked into source control, and is scoped with `--tables` (no recursive relationship descent). -- Types are versioned snapshots of server schema; regenerating is the sync command. - -This matches the pattern used by Prisma, gRPC/protobuf, boto3-stubs, and the .NET Dataverse SDK itself (see Appendix A). - ---- - -## 3. Why asymmetric - -### 3.1 Client-side: the agent-grounding argument has weakened - -The strongest original argument for client-side typing was that agents would make fewer schema mistakes. Observed behavior of the shipping Dataverse Skills plugin contradicts this — agents write correct Python against the dict API on common scenarios. - -Grading what remains: - -| Claim | Holds once agents are already competent with dicts? | -|---|---| -| IDE autocomplete on 500-field entities | No — agents don't use IDEs | -| mypy / pyright catches typos pre-deploy | Only if CI runs typechecking on the agent-authored call sites, which most teams don't | -| Refactoring support on column renames | No — agents regenerate code rather than refactoring it | -| Self-documenting schema in source | Weak — duplicates metadata available via MCP and the portal | -| .NET SDK parity | Aesthetic; the .NET SDK's early-bound value was human-IDE autocomplete, an agent-era artifact | -| Less agent hallucination | Contradicted by observed behavior | -| Token efficiency on complex multi-entity workflows | Holds — types in workspace eliminate exploratory metadata round-trips | -| Enterprise / ISV long-term code maintenance | Holds — stable contracts and static verifiability matter at scale | - -The remaining value is real but narrower than the initial pitch. Whether it justifies the investment is an empirical question. - -**Proposed evaluation** (1–2 weeks, cheap relative to the 6–8 weeks of post-GA work a typed-client layer would require): - -- **20 representative tasks**: simple CRUD, multi-entity workflows, polymorphic lookups, option-set enums, bulk operations, tenant-custom schemas. -- **Three API arms, answering two distinct questions**: - - **(A) dict + current fluent `QueryBuilder`** — *reversibility check* on the SQL-style commitment in §3.3. If A materially beats B, the plan to remove fluent at GA should be reopened. - - **(B) dict + SQL-style** — the GA baseline. - - **(C) typed + SQL-style** — the additive typed layer under evaluation. -- **Measure**: task success rate, tokens consumed, correction cycles, wall-clock. -- **Decision thresholds** (concrete, not qualitative): - - **Ship typed client-side only if (C) vs (B)**: task-success parity (within ±3 pp) AND tokens ≥15% lower **OR** correction cycles ≥20% lower. - - **Reconsider the SQL-style commitment only if (A) vs (B)** clears the same bar in favor of A. Expected outcome: parity or minor B advantage; the industry precedent in §3.3 would need direct contradiction to move. - - **If results are mixed** (e.g., C wins on tokens but loses on success): hold the typed client. The Protocol seam (§4) keeps the option open for re-evaluation with better tooling later. - -### 3.2 Server-side: production reliability stands independently - -Plugin code runs on live business events. Five structural asymmetries make left-shifted error detection more valuable server-side than client-side: - -1. **Test coverage is structurally harder.** Plugin context, pre/post-images, sync-vs-async triggers, and organization-service state are complex to mock. Agent-generated plugins rarely ship with comprehensive tests; script tests are easier and typically have better coverage. -2. **Silent-failure modes are common.** Plugin code can catch-and-log exceptions and continue in corrupted state. Scripts crash loudly and immediately. -3. **Blast radius differs by an order of magnitude.** Script failure → developer fixes script. Plugin failure → failed business transaction, possibly across many records, possibly customer-visible. -4. **Feedback-loop latency.** Script error: seconds (someone is watching the terminal). Plugin error: minutes to days (trace log, incident triage). Types collapse detection to edit time regardless. -5. **Deploy / rollback cycle.** Plugins take minutes to re-register and re-deploy. Every typo caught at typecheck-time saves real wall-clock. - -None of these are absolute — good tests and CI can catch typos without types. But the expected value of left-shifted detection is meaningfully higher server-side. This is the same argument that has kept early-bound entities the dominant plugin-authoring style on the Dataverse .NET SDK for 15 years, and it is independent of how well or poorly agents write plugin code. - -**Costs we accept on the server side.** Typing is not free. Shipping server-typed adds (a) a generator we must keep aligned with client-side codegen, (b) shared-package release coordination so client and server stay on compatible Entity contracts, (c) generated files in plugin authors' repos that require re-running the generator on schema changes, and (d) documentation surface covering both typed-only and dict-interop patterns. These are real, but each is bounded: the generator is shared with the client (§3.5), and schema-drift handling is already required for any plugin that reads metadata. The production-reliability wins outweigh these specific costs; the calculus would be different client-side, which is why §3.1 exists. - -**What "ship typed server SDK" means concretely** — same `Entity` base class and field descriptors as the client-side typed layer (if one ships) or as the shared model package regardless; SQL-style query surface (§3.3); generator CLI; a starter set of pre-generated platform-entity types (§5.5 applies); plugin-context execution model that accepts both Entity instances and dicts for the first release. Appendix B points at a prototype demonstrating this shape. - -### 3.3 Query syntax: one idiom, both SDKs - -Current divergence: - -| | Client SDK shape | Server SDK shape | -|---|---|---| -| Read | `client.query.builder(Account).where(Account.name == "X").execute()` | `ctx.dataverse.sql(select(Account).where(Account.name == "X")).execute()` | -| Create | `client.records.create(Account(name="X"))` | `ctx.dataverse.sql(insert_into(Account).value(Account.name, "X")).execute()` | -| Update from fetched | `client.records.update(obj)` | `ctx.dataverse.sql(update(Lead).from_entity(payload).where(Lead.leadid == id)).execute()` | - -Both compile to the same conceptual query; only the surface differs. Users and agents copying code across the boundary feel the seam. - -**Recommendation: adopt SQL-style (`select` / `insert_into` / `update` / `delete`) as the one primary query idiom in both SDKs.** - -Reasons: - -1. **Re-learnability is zero** — plugin code and script code look identical. -2. **SQLAlchemy 2.0 deliberately moved in this direction**, from fluent `session.query(User).filter(...)` to `select(User).where(...)`. Stated rationale: maps more directly to SQL, composes better, more explicit about execution. -3. **SQL is universally known** by developers and by LLMs — no new vocabulary. -4. **Client SDK is pre-GA.** Beta callers are signing up for churn; the cost of changing the recommended surface now is bounded. - -The existing fluent `QueryBuilder` in the beta is **removed before GA**. One programmatic query builder, not two. Beta users migrate via a mechanical rewrite, publishable as a codemod (an AST-based code-transformation tool — LibCST / Bowler in Python); the transformation is a 1:1 method-to-function mapping, e.g. `builder("account").filter_eq("statecode", 0).execute()` → `select("account").where(eq("statecode", 0)).execute()`. The beta population is small (~500 users) and signed up for churn; the permanent two-surface documentation/test/agent-training cost of keeping both outweighs the one-time migration burden. - -### 3.4 The consolidated GA surface - -Everything the SDK ships at GA, and why each surface is defensible once the fluent `QueryBuilder` is cut: - -| Surface | Why it stays | -|---|---| -| **SQL-style query** (`select(...).where(...).top(...)` etc.) | The one programmatic query builder | -| **FilterExpression primitive** (`eq`, `gt`, `contains` + `& \| ~`) | Shared building block consumed by `where(...)`; already shipping, reused unchanged | -| **Imperative CRUD** (`client.records.{create, update, get, delete, upsert}`) | Single-record ergonomics; the `session.add(obj)` vs `insert(...)` split in SQLAlchemy | -| **Raw SQL string** (`client.query.sql("SELECT …")`) | Power-user escape hatch at a different abstraction level | -| **DataFrame adapter** (`client.dataframe.*`) | Output-shape concern, not a competing query syntax; accepts SQL-style queries | -| **Metadata operations** (`client.tables.*`) | Unrelated to data query/write | -| **File upload** (`client.files.*`) | Unrelated to data query/write | - -Two adjacent cleanups to do while the query surface is in motion: - -1. The existing `QueryBuilder` already has two internal ways to filter — 15 `filter_*(column, value)` methods and `where(FilterExpression)`. Only the `where(...)` form carries into SQL-style's surface. The `filter_*` method variants do not need to be reproduced. -2. `client.dataframe.*` should accept SQL-style queries (`client.dataframe.query(select(...).where(...))`) rather than carry its own query DSL. - -### 3.5 What is shared between client and server SDK regardless - -These pieces belong in a single package consumed by both SDKs: - -- `Entity` base class and field descriptors (`Text`, `Integer`, `Money`, `Lookup`, `Picklist`, `Boolean`, `DateTime`, `Guid`, …) -- `Record` read-wrapper with `.id`, `.table`, `.etag`, dict-like access -- `FilterExpression` tree and class-level operator overloads (`Account.name == "X"` → expression object) -- `to_create_payload()` / `to_update_payload()` helpers that strip non-writable fields -- The SQL-style free functions (`select`, `insert_into`, `update`, `delete`) -- **One** metadata-driven code generator, **one** output file format - -Per-transport compilation (OData `?sql=` on client; query expression / fetch XML in-process on server) stays in each SDK's `operations/` layer. - -Two generators emitting subtly different Python is the concrete failure mode to avoid. - -### 3.6 Metadata-management boundary (server SDK) - -Server SDK v1 is read/write-data-only. Plugins do not mutate schema. This matches the .NET plugin model. Revisit in a later release if a schema-as-code story emerges. - ---- - -## 4. The `DataverseModel` Protocol — the one pre-GA change - -The single typing-related change worth making before client SDK GA: - -```python -from typing import Any, ClassVar, Protocol, Self, Union - -class DataverseModel(Protocol): - """Any class that can be passed to a Dataverse write operation. - - Generated entity classes implement this protocol. Users may also hand-roll - their own models (dataclass, Pydantic BaseModel, etc.) and pass them - interchangeably with raw dicts. - """ - __entity_logical_name__: ClassVar[str] - __entity_set_name__: ClassVar[str] - - def to_dict(self) -> dict[str, Any]: ... - @classmethod - def from_dict(cls, data: dict[str, Any]) -> Self: ... - -# Widened signatures — dict callers unchanged; typed callers supported -Payload = Union[dict[str, Any], DataverseModel] - -def create( - table_or_model: Union[str, type[DataverseModel]], - data: Union[Payload, list[Payload]], -) -> Union[str, list[str]]: ... -``` - -**No code generator ships. No generated classes ship. No user-visible behavior changes.** Existing dict callers continue to work unchanged. - -Why it is worth the pre-GA effort: the Protocol is the permanent contract between the SDK and any future typed-codegen approach. If it ships in GA, codegen can land in a minor release without a public-API change. If it does not, every future typed-API approach requires a signature-widening and a deprecation cycle. Cheap now, expensive to retrofit. - ---- - -## 5. Implementation considerations - -### 5.1 Codegen will not cascade into the whole org - -Navigation-property fields reference target tables by **logical-name string**, not by imported class. Users list entities explicitly (`--tables a b c`). No recursive descent into the relationship closure. Same mechanism as protobuf's explicit imports. - -### 5.2 Generated file trees are not large - -A generated entity file is roughly 20 KB. 500 entities ≈ 10 MB. Non-issue for disk, git, or IDE indexing. Users generate only the tables they use. - -### 5.3 Schema drift is detectable and reviewable - -Dataverse's `RetrieveMetadataChanges` API supports e-tag-based incremental metadata sync. Generator re-runs are fast. Three sub-cases: - -| Change | Dict API | Typed API | -|---|---|---| -| Server adds column, caller doesn't reference it | Harmless | Harmless (typed access unknown, dict access via `record.data[...]` still works) | -| Server adds column, caller wants to use it | Works immediately | Re-run generator; commit diff | -| Server removes column that caller references | 400 at runtime | Re-run generator, code fails at typecheck OR runtime — strictly more information than the dict case | - -### 5.4 "What is the source of truth?" - -The server is the source of truth for platform-managed metadata. The generated Python file is a versioned snapshot — same pattern as `git submodule`, `go.sum`, or a generated gRPC stub. The local file is authoritative for compilation; the server is authoritative for the live schema. They can drift, and the generator is the sync command. - -For user-defined custom entities (a future schema-as-code direction), local files could be push-authoritative. Out of scope for this decision. - -### 5.5 Pre-shipped out-of-box entity types (conditional) - -Applies only if the client-side evaluation justifies a typed client SDK. - -Concern: Dataverse schemas are tenant-specific. Every org has different custom columns on the same "platform" entity. A pre-shipped `Account` class might mislead users about what their schema actually contains. - -Resolution: - -- Ship types covering **platform-guaranteed columns only** (`accountid`, `name`, `createdon`, `modifiedon`, `statecode`, `statuscode`, `ownerid`, …). -- Each shipped file carries a docstring: *"This class contains platform-guaranteed columns only. Run the generator to extend with tenant-specific custom columns."* -- Typed access for unknown fields falls through to `record.data[...]` — no correctness cliff, only missing autocomplete for tenant-custom columns. -- The model is boto3-stubs: cover the stable core; users accept that custom/evolving surface requires regeneration. - -If false-completeness concerns show up in practice, deprecate the pre-shipped set and require user-side regeneration. Cheap to reverse. - -### 5.6 Security - -The server SDK's SQL-style surface routes through the same governed query path as the client. Parameterization is mandatory; there is no raw-SQL executor. The typing decision is orthogonal to injection defense. - ---- - -## 6. Non-goals - -- Shipping a runtime metadata cache -- Auto-generating the full relationship closure -- Converging the C# and Python SDK surfaces -- Requiring users to run a code generator before their first script - ---- - -## 7. Follow-ups - -1. **Evaluation design and execution.** 20 representative tasks × 3 API arms. Single highest-leverage action; determines whether client-side typing ships at all. 1–2 week window. Needs an owner. -2. **Pre-GA Protocol seam implementation.** ~1 week. Blocker only for permanent typed-codegen optionality at GA time. -3. **SQL-style query surface on the client SDK.** Built on the existing `FilterExpression` primitives; replaces the beta's fluent `QueryBuilder`. Ships at GA. GA timeline may need adjustment to accommodate; accepted in exchange for a clean single-query-idiom contract. Needs a codemod for beta users. -4. **Shared model package** (Entity, field descriptors, filter expressions, SQL-style functions). Owner, repo, release cadence — blocker for client/server symmetry under every path. -5. **Generator consolidation.** Two exploratory generators exist; reconcile to one shared tool with the best of each. -6. **Generator distribution.** PyPI extra (`pip install PowerPlatform-Dataverse-Client[gen]`) vs. a separate `dataverse-gen` package. Lean toward extra. -7. **Plugin metadata boundary.** Confirm server SDK is read/write-data-only for v1. -8. **GA version number and release cadence.** Does GA ship as `1.0.0` (stable-API signal) or continue the current `0.1.x` line? Post-GA minor cadence? This doc defers to the SDK team's PyPI/release policy. -9. **Codemod tooling and publication.** Which codemod framework (LibCST, Bowler, other) and where is it distributed (PyPI package? `pip install PowerPlatform-Dataverse-Client[migrate]`? a one-shot script in the repo)? - ---- - -## Appendix A — Prior art - -| Ecosystem | Typing approach | Lesson | -|---|---|---| -| .NET Dataverse SDK (15+ years) | Build-time: `CrmSvcUtil` / `pac modelbuilder build` emits early-bound classes. Late-bound `entity["name"]` remains alongside forever. | The closest structural precedent. Both idioms coexist; neither was ever deprecated. | -| Prisma (TypeScript) | Build-time: `prisma generate` emits a typed client from `schema.prisma`. Zero runtime generation. | Widely cited as best-in-class backend DX. | -| gRPC / protobuf | Build-time: `.proto` → generated Python classes. Runtime generation is explicitly unsupported. | Industry default for cross-language contracts. | -| boto3-stubs / mypy-boto3-builder | Stringly-typed at runtime; separate PyPI package ships pre-generated `.pyi` stubs. | Demonstrates additive typed layer on a scripting-first SDK. | -| SQLAlchemy `automap_base()` | Runtime reflection from a live DB schema. | Cautionary anti-pattern; consistently the least-used, least-trusted part of SQLAlchemy because static analyzers cannot see the result. Same failure mode as Option B-runtime in §2.2. | - -Two observations from the list: - -1. Every well-regarded typed SDK generates at **build time**. We could find no successful counter-example. -2. The closest structural analogue to Dataverse — its own .NET SDK — ships both early-bound and late-bound and has kept both. That is the right model for Python too. - -## Appendix B — Existing prototype references - -These branches and packages are exploratory prior work, not plans of record. Each has design ideas worth borrowing; none is the committed direction for either SDK. - -- **Client SDK main** (`microsoft/PowerPlatform-DataverseClient-Python` at v0.1.0b8): dict API, fluent `QueryBuilder` with composable filter expressions, SQL endpoint via OData `?sql=`, Pandas bridge, batch operations, upsert with alternate keys, file upload, context-manager connection pooling. -- **Client SDK typed-layer prototype branch** (`users/*/typed_entity_model`): working additive typed layer — `Entity` base class, field descriptors (`Text`, `Integer`, `Money`, `Lookup`, `Picklist`, `Boolean`, `DateTime`, …), class-level operator overloads, generator CLI, ~1,300 unit tests passing. All operations accept `Union[str, type[Entity]]`. Demonstrates the additive shape from §2.3. -- **Server-side plugin runtime prototype** (`Dataverse.Sandbox.Runtime`): typed-first, SQL-style query surface, pre-generated types for a starter set of ~16 platform entities, metadata-driven generator tool, plugin-context execution model. - -## Appendix C — Rejected options - -- **Runtime type generation.** Defeats static analysis; introduces cold-start tax; silent schema drift; breaks offline testing. Types built via `type(...)` from a live metadata fetch are opaque `Any` to mypy/pyright/Pylance. -- **Mandatory typed-only client SDK (hard cutover).** Breaking change for existing dict callers with no benefit over the additive shape. -- **Two generators, two Entity models, two output formats.** Subtly different emitted Python across client and server is the specific failure mode that reintroduces the cross-context re-learning cost we are trying to remove. \ No newline at end of file diff --git a/docs/proposal-typed-model-alignment.md b/docs/proposal-typed-model-alignment.md deleted file mode 100644 index fdb53031..00000000 --- a/docs/proposal-typed-model-alignment.md +++ /dev/null @@ -1,277 +0,0 @@ -# Proposal: Typed Entity Model — Alignment with Server SDK and GA Scope - -**Status:** Draft for discussion -**Date:** 2026-04-20 - ---- - -## Background - -The Python **client SDK** (`PowerPlatform.Dataverse`, `main` branch) uses a string-based programming model: table names, column names, and filter expressions are all plain strings at runtime. The **server SDK** (`Dataverse.Server.Runtime.Extension`) uses a strongly typed entity model where each table is a generated Python class, fields are typed descriptors, and queries are expressed as composable condition objects. - -A prototype branch explored what it would take to add typed entity support to the client SDK. It produced a working implementation — but the baseline for this proposal is the **`main` branch**, not the prototype. The prototype is evidence of feasibility and reveals the specific gaps. - ---- - -## API Comparison: Client SDK vs. Server SDK - -The table below covers both SDKs as they exist today (`main` branch for the client, current state for the server). - -| Category | Feature | Client SDK | Server SDK | -|---|---|---|---| -| **Entity / Type Model** | Entity base class | No (main); prototype branch adds it | Yes — `Entity` with `_logical_name`, `_entity_set`, `_primary_id`, `_primary_name` | -| | Field descriptors | No (main); prototype adds `Text`, `Memo`, `Integer`, `BigInt`, `DecimalNumber`, `Double`, `Money`, `DateTime`, `Guid` | Yes — same set | -| | `Lookup` / `CustomerLookup` | No (main); prototype adds both | Yes | -| | `PicklistBase` / `PicklistOption` / `MultiPicklist` | No (main); prototype adds all | Yes | -| | `BooleanBase` / `BooleanOption` | No (main); prototype adds both | Yes | -| | `Entity.as_dict()` | No (main); prototype adds | Yes | -| | `Entity.to_create_payload()` / `to_update_payload()` | No (main); prototype adds | Yes | -| | `Entity.from_dict()` | No (main); prototype adds | Yes | -| | `Entity.fields()` | No (main); prototype adds | Yes | -| | Entity class declaration syntax | N/A (main); prototype adds keyword-arg style: `class Foo(Entity, table="…")` | Plain class attributes only: `_logical_name = "…"` | -| **Query / Filter DSL** | Comparison operators on field descriptors (`==`, `!=`, `>`, etc.) | No (main); prototype adds via `_ComparisonFilter` | Yes — via `Condition` | -| | Logical composition (`&`, `\|`, `~`) | Yes (on `FilterExpression` objects, string-based) | Yes (on `Condition` / `CompositeCondition`) | -| | Filter rendering target | OData `$filter` strings | Parameterized SQL fragments (`.to_sql()`) | -| | `filter_in` / `filter_not_in` | Yes | No | -| | `filter_between` | Yes | No | -| | String function filters (`contains`, `startswith`, `endswith`) | Yes | No | -| | Null checks | Yes | No | -| | Raw filter passthrough | Yes | No | -| **CRUD Operations** | Single record create | Yes — `client.records.create(table, dict)` | No | -| | Bulk create (`CreateMultiple`) | Yes | No | -| | Single record read | Yes — `client.records.get(table, id)` | No | -| | Multi-record read (paginated) | Yes | No | -| | Single record update (PATCH) | Yes | No | -| | Bulk update (`UpdateMultiple`) | Yes | No | -| | Single record delete | Yes | No | -| | Bulk delete (`BulkDelete` async job) | Yes | No | -| | Upsert (alternate key) | Yes — `client.records.upsert()` with `UpsertItem` | No | -| | Typed entity class accepted in place of table string | Prototype only | N/A — no execution layer | -| **Query Execution** | OData `$filter` queries | Yes | No | -| | SQL queries (read-only, constrained subset) | Yes — `client.query.sql()` | No | -| | `$expand` (related record navigation) | Yes | No | -| | `$orderby` / `$top` / `$count` | Yes | No | -| | Pagination (`$skiptoken`) | Yes | No | -| | Fluent QueryBuilder | Yes | No | -| | `QueryBuilder.to_dataframe()` | Yes | No | -| | Aggregations (GROUP BY, SUM, AVG, COUNT) | No | No | -| | Joins | No (`$expand` only for navigation properties) | No | -| | FetchXML | No | No | -| **Table / Metadata Management** | Create table | Yes — `client.tables.create()` | No | -| | Delete table | Yes | No | -| | Get table info | Yes | No | -| | List tables | Yes | No | -| | Add / remove columns | Yes | No | -| | Create 1:N relationship | Yes | No | -| | Create N:N relationship | Yes | No | -| | Create lookup field | Yes | No | -| | Delete relationship | Yes | No | -| | Alternate key management | Yes (create / get / delete) | No | -| **Batch & Transactions** | OData `$batch` multi-request | Yes — `client.batch` | No | -| | Atomic changesets (all-or-nothing) | Yes — `batch.changeset()` | No | -| | Content-ID cross-referencing within changeset | Yes | No | -| | Max operations per batch | 1000 | N/A | -| **DataFrame (pandas)** | `client.dataframe.get()` → `DataFrame` | Yes | No | -| | `client.dataframe.create()` from `DataFrame` | Yes | No | -| | `client.dataframe.update()` from `DataFrame` | Yes | No | -| | `client.dataframe.delete()` from `Series` | Yes | No | -| **File Operations** | File column upload | Yes — `client.files.upload()` (small + chunked) | No | -| | File column download | No | No | -| | File column delete | No | No | -| **Code Generator** | Generate entity classes from Dataverse metadata | No (scaffold exists, not implemented) | Yes — `generator.generate(org_url, entities, credential, output_dir)` | -| | Generates picklist / boolean / intersect types | No | Yes | -| | Auto-fetches Lookup and M2M dependencies | N/A | Yes | -| | Output imports from shared base classes | N/A | Yes — imports from `core` | -| **Annotations** | Formatted values (`OData.Community.Display.V1.FormattedValue`) | Yes — `include_formatted_values()` | No | -| | Custom OData annotation patterns | Yes — `include_annotations(pattern)` | No | -| **Diagnostics** | HTTP request/response logging | Yes — opt-in via `log_config` | No | -| | Automatic header redaction in logs | Yes | No | -| **Change Tracking** | Delta queries / change tracking | No | No | -| **Real-time / Push** | Webhooks, server-sent events | No | No | - -### Reading the table - -**Client SDK** is a full HTTP execution SDK — it handles auth, HTTP, OData serialization, pagination, and bulk operations. Its gap today is the typed entity model (no generated classes, no field descriptors, no type-safe query DSL). - -**Server SDK** is a type-system and code-generation framework with no execution layer. Its strength is the generator and the richness of the typed entity model. It has no way to actually send a request to Dataverse. - -The two SDKs are currently **complementary, not overlapping**: the server SDK produces the types; the client SDK executes the operations. The alignment question is whether the type layer can be shared so that entity classes generated by the server SDK's generator also work as typed arguments to the client SDK's execution layer. - ---- - -## The Four Decisions - -1. **Alignment**: Should the client SDK adopt a typed entity model, and if so, how closely should it align with the server SDK's contract? -2. **Divergence risk**: What happens if both SDKs evolve their entity models independently over time? -3. **Lock-in and GA timing**: If the string model ships at GA and the typed model ships later, how difficult is it for developers to switch — and does the timing of GA matter? -4. **Shared schema library**: Could both SDKs share a common package for entity/field descriptor definitions? Is that feasible with the current layout? - ---- - -## Current State of the Two SDKs - -### Client SDK (`main` branch) - -| Layer | Current state | -|---|---| -| Table/column names | Plain strings everywhere | -| Filter expressions | OData strings (e.g., `"statecode eq 0"`) | -| Record access | `result.data["fieldname"]` dict access | -| Type safety | None — no IDE completion, no compile-time checks | -| Code generator | Not present | - -### Server SDK - -| Layer | Current state | -|---|---| -| Table/column names | Typed Python classes (generated) | -| Filter expressions | Composable `Condition` objects that render to SQL | -| Record access | `account.name` typed attribute access | -| Type safety | Full — IDE completion, Pylance inference | -| Code generator | Implemented (`generator/`) | - -### Key structural differences - -The server SDK's `core/` layer (`entity.py`, `datatypes.py`, `picklist.py`, `boolean.py`, `lookup.py`) is already logically a standalone schema library — it has no HTTP, OData, or Dataverse API dependencies. However, the two SDKs differ in ways that matter for alignment: - -| Concept | Server SDK | Client SDK (main) | Notes | -|---|---|---|---| -| Entity base | `Entity` with `_entity_set`, `_primary_name` | No typed entity model | — | -| Condition rendering | `Condition.to_sql()` — SQL fragments | OData strings | Fundamentally different targets | -| Payload methods | `to_create_payload()` returns `Entity` instance | Not present | Server returns typed objects; client needs HTTP-serializable dicts | -| Boolean type | `BooleanBase` + `BooleanOption` | Not present | Server SDK naming already uses `BooleanBase` | -| Picklist type | `PicklistBase` + `PicklistOption` | Not present | Naming is aligned | -| Generator | Implemented | Not present | Both would need to produce from same base classes | - -The condition rendering difference is the most significant: server-side execution speaks SQL; client-side execution speaks OData. A shared base class can define the operator overloads (`==`, `>=`, etc.) but the rendering backend must be environment-specific. - ---- - -## Decision 1: Should the Client SDK Adopt a Typed Model? - -The prototype demonstrated that the typed model can be added to the client SDK as a purely opt-in, backward-compatible layer — the string-based API is unaffected. The benefit is real: IDE completion, refactor safety, no string duplication across schema, create/update/query consistency. - -The question is not whether to add it, but whether to add it in a way that is aligned with the server SDK or in a way that is independent. - -**Recommendation: Yes, aligned.** Independent implementation is the path to the divergence problem described in Decision 2. - ---- - -## Decision 2: Lock-in and GA Timing - -The string model and the typed model are not equally easy to adopt at any point in time. There is an asymmetry: switching from strings to typed entities requires active code changes, while the reverse (typed to strings) rarely happens. This creates a one-way lock-in dynamic. - -### What developers build on top of the string model - -Once a team adopts the string-based client SDK, they tend to build their own abstractions on top of it: -- Configuration files or constants holding table and column name strings -- Wrapper functions like `def get_accounts(client, select): return client.records.get("account", select=select, ...)` -- Generic utilities parameterized by table name strings -- Automated tooling that generates string-based calls from metadata - -None of these abstractions migrate cleanly to typed entities. A typed model requires that the *entity class itself* be the unit of schema definition — string-based wrappers are structurally incompatible, not just inconvenient. - -### The adoption momentum problem - -GA is not just a version number — it is a signal to developers that this is the stable, recommended way to build. Whatever model is present at GA becomes: -- The pattern in the first blog posts and tutorials -- The model in internal starter templates and onboarding guides -- The shape of the first production codebases - -If the string model is the only model at GA, early adopters build on strings. When typed entities arrive later, those developers face a real refactoring cost to switch — and many will not. The string model becomes entrenched not because it is better, but because it came first. - -This is different from a typical backward-compatible addition (e.g., adding a new method). Adding a new optional parameter does not require existing callers to change anything. Adding typed entities as an *alternative* to strings means developers must actively choose to rebuild what they already have. The later typed entities arrive, the more code already exists that won't be rebuilt. - -### The cost of the switch, concretely - -Given the prototype work, the migration from string model to typed model for an existing codebase involves: -1. Defining (or generating) an entity class for every table the codebase touches -2. Replacing every string-literal table name with the class reference -3. Replacing every string-literal column reference in `select`, `filter`, `orderby` with the typed field or string equivalent on the class -4. Replacing dict-based record access (`result.data["fieldname"]`) with typed attribute access - -Steps 1 and 4 are the expensive ones for large codebases. Step 1 requires the generator to be available. Step 4 requires touching every consumer of query results. Even with the coexistence approach (strings still work), there is no incremental migration path — you either have an entity class for a table or you don't. - -### Implication for GA timing - -The typed model does not need to be the *only* model at GA, but it needs to be present and recommended at GA if it is the intended long-term programming model. A typed model that ships 6–12 months post-GA will face an installed base that has no reason to migrate, regardless of how good the model is. - -**Recommendation:** If the typed entity model is the intended long-term direction, it must ship at GA or within a short post-GA release that is clearly signaled before GA. Shipping the string model as the only model at GA without any typed entity support, and without explicitly communicating that typed entities are coming, risks cementing the string model as the community standard. - ---- - -## Decision 3: Divergence Risk - -If both SDKs independently define their own `Entity`, `Text`, `PicklistBase`, etc., divergence is guaranteed over time: - -- **Field type drift**: a new field type added to one SDK (e.g., a `RichText` or `BigInt` descriptor) will not appear in the other unless both teams coordinate every change. -- **Generated code portability**: if a user generates entity classes using one SDK's generator, those classes will not work with the other SDK. A developer running code both client-side (HTTP calls) and server-side (plugin execution) would maintain two sets of entity classes. -- **Parameter drift**: the prototype already found one divergence (`DateTime.format=` on the server vs `DateTime.date_format=` on the client prototype). Without a shared source of truth, these accumulate. -- **Behavioral drift**: `to_create_payload()` returning a typed `Entity` vs. a plain `dict` — if both SDKs add payload helpers independently, their signatures will diverge and user mental models will break. - -The cost of divergence grows with adoption. Before GA, fixing it is cheap. After GA, it is a breaking change. - ---- - -## Decision 4: Shared Schema Library - -### What could be shared - -The schema definition layer — `Entity`, `_FieldBase`, `Text`, `Integer`, `DateTime`, `PicklistBase`, `PicklistOption`, `BooleanBase`, `BooleanOption`, `Lookup` — has no runtime dependencies. It is pure Python. Both SDKs use it only for: - -1. Defining entity class schemas (descriptor protocol) -2. Schema introspection (`Entity.fields()`, `Picklist.options()`) -3. Operator overloads for building filter/condition expressions - -This layer is a natural candidate for extraction into a standalone package, e.g., `microsoft-dataverse-schema`. - -### What cannot be shared (environment-specific) - -| Layer | Server SDK | Client SDK | -|---|---|---| -| Condition rendering | `.to_sql()` → SQL + params | `.to_odata()` → OData filter string | -| Payload serialization | Return `Entity` instance | Return `dict` for HTTP body | -| Code generator runtime | Calls OData metadata API via `requests` | Same, but different output templates | - -Each SDK would subclass or extend the shared base to add its own rendering layer. The shared package defines `_FieldBase.__eq__` etc. and returns an abstract `Condition` object; each SDK provides the concrete renderer. - -### Is this feasible with the current layout? - -**Technically: yes, with modest refactoring.** The server SDK's `core/` module is already structured as a self-contained library. The changes needed are: - -1. **Extract `core/` from the server SDK** into its own repository/package with its own versioning. -2. **Refactor client SDK**: replace `src/PowerPlatform/Dataverse/models/` (as prototyped) with an import of the shared package. The client SDK extends the shared `Entity` and `_FieldBase` with OData rendering. -3. **Align naming differences**: `_entity_set` vs `_entity_set_name`, `BooleanOption` naming, `DateTime.format` parameter — resolve these before extraction so the shared package has one canonical API. -4. **Shared generator**: the generator backend (metadata fetch, normalization) can remain SDK-specific; but the code generation templates that emit entity classes should produce code that imports from the shared package. - -**Organizationally: this is the harder part.** A shared package requires: -- An owner (which team? both?) -- A release process independent of both SDKs -- A versioning policy so both SDKs can consume updates without coupling their release schedules -- Agreement on the naming discrepancies before they become public API - -The prototype demonstrates that the descriptor design is sound. The risk is not technical; it is governance and coordination. - ---- - -## Recommendation Summary - -| Decision | Recommendation | -|---|---| -| Adopt typed model in client SDK? | **Yes.** The prototype proves it is backward compatible and low-risk to add. | -| Align with server SDK? | **Yes, intentionally.** Independent implementations guarantee divergence and impose a future migration cost on developers who use both SDKs. | -| How to align? | **Extract a shared `microsoft-dataverse-schema` package.** Both SDKs import from it; each adds its own rendering layer (OData vs. SQL) on top. | -| Lock-in / GA timing | **The typed model must be present at GA, or clearly committed with a near-term date.** Shipping the string model as the only model at GA without typed entities risks cementing string-based patterns as the community standard before the typed model arrives. The migration cost only grows after GA. | -| Push GA for this? | **Yes, if the typed model is the intended long-term direction.** The naming alignment fixes (DateTime parameter, BooleanOption naming) are small. The generator is the main scope item — without it, typed entities are incomplete for connecting to existing large schemas. The correct question is not "is a slip worth it?" but "what pattern do we want the first wave of production code to follow?" If the answer is typed entities, they need to be at GA. | -| Worst outcome | Shipping GA with strings only, adding typed entities 6+ months later, and discovering the installed base has no migration incentive — leaving both models in the SDK indefinitely with neither clearly recommended. | - ---- - -## Open Questions for Discussion - -1. **Is the typed entity model the intended long-term programming model for the client SDK?** If yes, it must ship at or very close to GA. If no (strings remain the primary model indefinitely), the alignment work is lower priority. -2. **What is the acceptable GA slip to include typed entities?** The prototype took a few days to build. The generator and naming alignment are the remaining scope. A concrete estimate is needed to make the GA trade-off decision. -3. **Is there a customer scenario today where a user runs the same entity class code on both client and server?** If yes, the shared schema library is urgent. If no, the timeline pressure is lower but the design should still account for it. -4. Who owns the shared schema package? Is it a new repo under the same org, or does it live with one of the SDKs? -5. Are the naming discrepancies (listed above) acceptable breaking changes before GA, or do they need to be carried as deprecated aliases? -6. Should the generator be shared (one tool, two output modes: client vs. server), or remain separate tools that produce compatible output? diff --git a/docs_local/_build/.buildinfo b/docs_local/_build/.buildinfo deleted file mode 100644 index e403187f..00000000 --- a/docs_local/_build/.buildinfo +++ /dev/null @@ -1,4 +0,0 @@ -# Sphinx build info version 1 -# This file records the configuration used when building these files. When it is not found, a full rebuild will be done. -config: cc4b35bed06ca8e65bca5667af91bb7d -tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/claude_skill/index.doctree b/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/claude_skill/index.doctree deleted file mode 100644 index 208098ea68df19a5ee959b632c46afe32f21c057..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3639 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 Lm1a=P|IYq@!vrOz diff --git a/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/common/constants/index.doctree b/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/common/constants/index.doctree deleted file mode 100644 index cd88b8758be35d25d5fd9559db0c47fd5b3bc11d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30710 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 Fz5^(*4-fzV diff --git a/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/core/config/index.doctree b/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/core/config/index.doctree deleted file mode 100644 index 89881c46a9f02d6ac69dc8369a9d041df0e7b6f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 38957 zcmd5_36LCDdA6iIN7CxB)^-+&G}u@pTf0KC0kgJc99ec`*|IDf@F6qXJJY+}t+|-) zk+i`EB@|#)no5EvkgF2F+!U#zQlx?@L&6b?Tm^wjRe%CQD!3?48wyfDAm9Ivey`8z znOzX7(rowZ_ul`%|NYnd-*s;EkDvP|7qNfARIOF*`cB=OYqn~Z*9#^npY|L z@#~iMDY*Q2>x|VoUN`-vR;Mv{i|L!Et&V5SRa+hUX)Za-Jt~gUBllWo36ElhUf1L3 zj!b11*bPq2L&JjcYTfia zB;t9rX*T%NM6KJFFWq3=H<#(HXHVHP_D1_z_6B=r@R~#OCtvEJO()GotKM)>kA3ol z)oytwBW<4z+kU9-0O8r=E56-oo&?HXrQWKV^^<1TZ<%c;TJB_6?j+F+KSZ;WPP1m6 zo$GjL_^zYtyNkgVuWdWcvvX#xCi-@i<Scfj`HB6)KEZhDWi+)Q^qtW%v#kxh!^i6PZ z$AAnOj8!bdY}Sl&#{%P>wrYEf-@f`gpICSThI;}>I*IM8f=z9!Sp$}pvmI+m z@7^0%MLnrQ#_Nu6b!eYgj&@V;N-xNCQJPSl?cOHf*oVvR>|4 zQgnzz?gMAs3;mMHLctiC3Nh~m`-Oz>i!jK(5U+hC;rJm5-^ZmF>X-^QzyLkl3dZr~ zue70>M>!h7MxY*N912dsShbo!5>~=w_^Ul zwevTSy7Ib>hS^y;2tNbXIk-f)8F1C+w9ma@oAy>=hD#$!@K+`LyQ>Y=OtuLc!bb(A z(jl{6w>q;mYYFzP#w;Du4YN&F0ttT0Xm$O?Ru|TeoUGZKm*J-vwq@4fam|%Vq3YyH z2Q@O~DSQIMvwR=M#$%Q<%q#temc+*s&I5U{WO6mkh0kEwrVS>2yW=K&ZK@ACATkGMssgY z%)=AjI_7u?H0O1S4#%@3$lR#y-ao<^qc0@gDzSyTS}p+S_=!h%vu3k#uQ5*$7q>>Z>s z#f$D9Y7!e@icwMMR0LB|n4Ht6gOWl-t7m_#o^%hD2m=?EL8X2OCRIzq9!cPo+bO@c zoaSCBNK$}HvRn9Vu+cX`s_#VEsGP%B$3rC-8X|72is{iRG-U}w{t>!ZKXCuLi&!F(9;)UgqCzw3j zsLy+Clfu6fZ4qH&!;?>-oE!;(HW&>kOjQ8Ylwn0Z2@ys@dlEH>xM?_+${ECMxs$~o z4KR)mKh=|_E|IQ!_)~>@7~?wk<6&-w&I}a#-l#%PAH_w2rDGmO7#K4IH^sd!qs(G7 zhSMw(LEX~}hV$(r!BnkH!gIJP7(s9{IY2FA7&}E@DxQP5w%_gOOpJiF zHC~$zOoYIMNfK|07k&`-Zd!mhSa+&tO0EZUx=&N~8TUK*13u7bgYi8{gPb5i$akF~ zSrJbP;D+^49UE0I!MZb1fa@r`1gJzw$`6I*-46pB_X$D?7=DXm_!RwNz9Hk;xgpF`Xbdc}VWgLQ?gk`xT(Yv>~RMP<1aT5de(yFoUKB zkinRU(b}hFOxGR@CYHMOIzxc9E+|#}R<(k85z>OES=#HN;aRH6MojWQL<~M(u@l_& zb-^qelT^7n)tE%i;Qg8nf{w&G%pP(Oba$+ts9b6~fDM8ODyWq)2#VvR`dgPe0A%@j(VES9lS@m(bzD+>8n8Bhrb*Wl!c__>(sXLzU%^Ak69m|@fDO`h8`Ch}qghbuITq7I=Oq=dC zjvqOK$Uee!-R7xg>r9iuE@5ic=vc(?R!vP5%@5yjP1`QVdR60&%@O)YV5IKmBi@uR4Za`0ICC?3q-1@z`RmuTB8FGX~V3cu)hQc4M76ouW zVc;!l{R6my{X)3xU4#4f;ozS7Rr0ekEpZz6*9t~dD_j(mmK>{ItB}B@Xl6ur+4s~> zqA0Tin&mGYhqk~(4BHEtFwGcZ38YD1<*xSqQ{lwJzvH%wpZ6L@N3(zk-DFyzr_6l_mI zl(b0uq-gTx!Zi6Jr^$cd4^s{6jk3nal&p`twW#+dIpYnq7dTd-fpfn{<+x9?0pGP6 zpN`cS)mUMCC3~jY{SHGlT25NQtO(0bQAUff^qqa?0c-A>orV8{CXi8-6T(-5i$aR6 z(`H3kYJ_0Z!Km@|bC2mNEkV@j;F5Iq(dd>a6cRhQRI?X~&lW_dOa~W-yr;fuHD9_R z+Q;dXrRGzRHG#);FcGq}V!(K|Je4xzem3sl%2(Bh`J$AOn&m}QAk-}GziZIMq?q!b-uNC{>$P(6L_GbvoR8 z;;KjV&Rzq6pieyMfI+LSXi?Bi@VsJdq?jOHN4^HQjnO+3E2#lU zzzS=COo;B^fvurWLi1d<+wYe$1(Pg3?7 z_rv%Dbk=L+$%?xDYc2qH3ROhnd?M_|At-lDn3rkpt0?@xm|K15HfhWB#L6u>weQ{p z40N5!Aw-pGWVK$lQsqdei)A#5CSrxjE>?xzcc5uJK;_N@NB4UCD2_RZH-wr9d#2dk zm*6S5JT7r*^5Wji%BIASQ$^toJ@*hn`CsbvXb5W-yznm31K@|l$QG5f)BrtIatooL z3QG;8noqt@nfKLXs5SJsoLF_~j*p<-_n@6FaMU$BWKn9Y`aoRZ12 ze4WktV3YVL-?t%$2*`P1rY+5uOq2PjCW0qcH1*C?8@~o=WtLQEVxfhjZImpk5k&?C zGl=5WKyew2R1C2Z4QHR+P4u*jel~|sQ+PV>ZehP$+3z;?yPf@B%zk&!ZwzrZ2eV4^ zaWFPHKJdiLQ1if1tDxqRxSs286%y-_nW~Md;5D63ai7t(d!N~p>(E!amlpLb+@GVW zh*}lXC)c7_LB)MIA3~Pm<`B|(JXHriKZL`~+|Rca?+W}VIg8BsT*B2@N*f}s!vO-rU55l)DP(GWkSXXlB#}|Q;a&|Gg^Y?l#w?0U zs0)L^o?kDl0i~0!&?YqYL!r-#Hl$jEvKDL_dcA<4@*_O9QjNzkW_RSGY zq>CF%RYZMXVWNtW`@2veC2mN`y82a)Y7oW^L6k(?@JT8s;)eLHJL0h#qs&+&Zuk*~ zYP6hW;;gvgXHaH{xFOSLh#OK9pajCW;S0&7V`w0<5Q!Tm2$m9gNXhexUouB2&WHn7 zIx`l9gEE+LAJ3sRX~nnUJF@*H%jUive`0>Iij%#{_WgFHW3l~xOq3{)b*$C2m`L5l zwu_l{guu=^jc&teb{mV>Y}Q)B)EyRw8*q*(l*Sj)`yI3C(f&og<;^WTK+W zn{#*3vE^l2hsJA>XET&uZo`1J`9TRaEEKEPc~L5;qHp3QBk|WPI8*QsrnnSX&*q=KCr>H-t+T}V3 zK0oTZ6!bssp28m%6#}vt1Y$ia!_@sL{>TAOFhYKnBnW>|wAe8gC3$)yHCl+Si%;Z7 zA=2n6gveeL#484c_#70-90zCw-)U{ww@;eX*zhpa2iUnP`u833-Hw( z=_SS$-YBRl%BN~e^)tKTr8{;|524t26EkA?NB ztp=S@HD?^5jX-oT0K$GHoNohDv(Il~O^=+wgZfX6Ic!U`OG}79wX{?I*Z2MeV2Lc}b}DhtvU@>P=Ly)b>F8-al2eGnfib zK%EYb2*+^^)RB_WIGlX7ZszbuLak?3<^BalK4;uN?GxG(D@19Ir@;EK*Y~MStPhh@ zS#knw@a>g+eb}9mNtj|w!{TcKq=~SwGVJDDzm$(%;s;vIMR16%35z$h|LlKk28oYO zB6GT9FdW5?!Xpno?~FBHmxa}( zO?tckgeoFy2gIVU9Vk|C?ZET$A!Mx`;1EE-fOAjFmBLRUfc!?vQ9KPb>&6Bb$GI{v;?Zjeb(W*Y}whH0Gls6dyJmg zvNQHH?6NbauvfY4><)b*bM;2)CBqmc9feifr067Uib;A8u>dzo(Y#79-Y2+3c64-r zQFv|gyTWRiMY=O?kv2=G^Fe5CabAEMRW<4Bk)H@F9>_NREs7PiO`pz(kfmxkg#Nba z3%S+*ifz*XWO*tV?@TKqAVjm;q59N2r!qOb61{d->T0n{W9c_AIeg}KH@P3D!zAv;-D%MY7yp#sR z!}}d_=}2@X{NQ6@&JJO1Y@#kQ6n-7?yzY0%yvpx*17%tLNcgp3HO%k0C+>G_3H=VX zE@cqU<1Jy;19={gp;$rB;~n`BvQ!O+kYb(^Yi~Z73qYENfyTuG#!&WY0MdLi7xPR@ zA|OO9NxJv(xqJv&mSli7d?g>kz}*|SEZ@qlzew-yL=CL><;n4@a6UL;qWW6mkm`K& z6Wu|bkMYSojgrYS8&IsE^RXizLKe#i2t}Na=j21kVi^u0#rdEQx|Us4_o%||tHC&H z<9tZV(9ijh`K0r419e-}`M3wp$2lPrLpdL^@Q}{OQJ}<~k2~mzIUnrl!aE;w)!YSi zKIH1UwQ)Ye+LF$PNj$GRA2P3UKB|;u_0C5ntcE!s_phPz;e}NXnEF&NkaX$VpA3_$(a0n^R z2Yt}B?5es)6?Pv$y=&uqNXyXA`H=af^Kn@z;e6Z==i{Vk@KDZ&EIg$1u^T9H=c7zd z%=utX7t;B-T`d`1{d~*8Vdh&D0;`^F(U%(YkZ2_4aU;ks+zvgbayo9J46AoKt`DnW zPDdr~bX*@sJaXx!2%{GB4iX)?D@0=;2jwV=6?9Ne=0nI*M;tT zU3|H3Fa$nBMO_9U`NO%b%(P+xLJ`O3d_IIMD>gtI-kc9%V77@nK9A?tUxX9Z((&Ph zQO2>aA6lcb||Mu79P^+*|1SMJtOqQ zoF4XcA)TJP6KmI3@B19iwdENLAQO13+678ke4pY8{Ra9%ctR-!ludmW$%im-r^mgO*W}h;q@&l;Tj7LJ7Ok(h(oePr z^;Z5ommHZa^A;2<=&igbA3_$(2na>Il@H}Z$YL1|A;nvv54sv&RrjdE?sn8WsJHT* zxVMtDVcAYgim{Qt%V&X0%y+TiyX+86AIf);g@^QAz6#hpVDmM4V!jJ|x{$uho$BW6 z;hYwJgBD#^-Kt@&N}(%f<9#l1Shc&dOV(7z2`&eX7#y*pKC*G*j{f&y@wOGe6)Sp* z<3>gxUWZ&18A(&_(N6_)O8$y1Ud%et@y94u(Bqic6qWoKge*YTrxW}eqHBU>5s02lhnOtNGFH}zAg2R%xv%`|M(k3LU_I`EXAbSN zAHmtm2HZxRMJsJ*bb4VoVL9WbLg9CQ;gGIwrx`gWAhFV)0gG*Igyv4>#cA$$!AXPKl*?jrudch#HYJJyg#`%`<7mmV;EkAN zIRyszzF_%O%c389`NBgT3#S=k4|U`S4qQTrBQ%;-iyxbT8`s&Hi1LDTzG)gPF>G1X!MQy*-z0E^RwC0TKL(0F`vc8(CoognIG05j`mhZO0zw5bb1U&{FxAO z_B1Tw`qAwQ@n8$T&@w#M9v3_a_eOqVSMlIL4-cO*t_H?kysFBw{o>&AHm)@6wCko% z*MH5{Oy4|>D|{?~>Cg`yT7|BXkfPb=!t8UD9X~941o{R}R_0Nz&lJfquhs3~HduR4 zu!Eh23WRXzNCjtGEz@0yJ$Q7%sE@00>%F767)|D}YIyCEl&lM;s@)D9Z&DF;9Sb(e zl)8g+g^mRi(k}OcZM9al>pOLi+zfX7NiP^<_XsJMFW89lRN7r17litD@0dMxESSXg zjutM9Y;{oGB%04p(89~cPOF8SxSql}ixCm-Xk{(JiOtPZ_*@dN-A)~Eo9Wt7S}I9j zanaM$!S*E-$Bjvrmi%b28K+hHI5@U)mU;(w1Wn<_FQ<)(0d{hWhpL{%Q>?m0XSsM4 zI=-vIMlZNQ3XnvBx=9yGB4Ja%XjErcb;q{y2J5Y6FSxjaGekSJO3iB1u{q7E1IpskXUS2FuaIzy z{JwIBe*IuDLI;nau=C`44{_4?j*kjBAD>4HT2-gH z1Uv*|ZzoRox{J|lmrxbJ4)+@}b$}a>fkq9Cwy|TL0h`I!Z6x7fSKQ2QL*yEc-@9|` z?qCA0A-nYzXic>(I<9%if{v(iQJ|DX6YVFd3@)QxR^I%+eMt7^+AD~SH0N5K<$b+i znsPE+kh4PPHbXzLo7}DT?DL*~E&61tvs8^$PbsSUD`oYhG)ZPoS)Dn@@|PG4&|{zN zH|hX3iOc07#x>}iqZ7d{2t}h3YKO1^;`;3K!B%gj>6>ROwzF*4@gD*-LRZxHf=yln zHyc-8*@as1K(mY4JAbXPv&4{$?%5Sy-NpqT1ZU(#=~LZy5546y=*)PWwabMXj0>d( z?!k%HHE4nroEvCcmXEB{xaQV8Rbdy|R>F&H0el;EFc<`ETVLW@@qDm>f(f|sKA7NI z32FdL=`?E&M9i=7?G6mbZQu(Z^fW=s=(f8zZkc5 zj)~nF>aI|64n`P`cyVa5$vy|8jt86Gdz4(R6<{O0?-6?jHDbaIgEqp-ekVhTT|v)o zzjo;oZ+6X)OBWHjbT@=cw@0vJ4qQ5?f^9o=X=5VW1K`po4z?iAtzhvRo1u2E$BZzG z{<@c96wJo#EFjViHtX!hD7U%l@LTDL#d!*8CYg_Sk)Fcyp^;$E#$u};axlxfBbFHE zo{ZEuDWS4L+avYELUsJ!(RIOeye{ztUIO%Hwbdo9*b6q|vgbNyK`86-Ln8e#^zQ3= wCm_EFD?t;;`wd7gwukC7lDsiYX6(ArX;$mq8i~g;l`7T9ItXRRyK(OS0l;;$kpKVy diff --git a/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/core/errors/index.doctree b/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/core/errors/index.doctree deleted file mode 100644 index ea18a9db93ef24d1b9491fb5b78e1109200d988c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 103492 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�dXgib zdi?>^8KHW80MtuK%%NTxv8B|@aX+J8jxt;P>Xid!8YO8Gv_d^X$UUb*m1XEBx(z3S z_%&+Z)IxhoRG1PKrl7)}LaiI)Oi8y@2c4SRZ>)h}ai2=5I8PJLgn-5^Ew1m8(3KE0 zl-kM*UB&{?7$VbpC*Id#A!(@skA#)EF1k%@2BK4^qqN><6|Tp+rsc3b zRP-|TyoM=9Mvw8bsdb^TXA$c;onxG8(ROkn&auF{YS)us@;S!_)ES{3|2NQMB{7E{ zXT+A$W5@lB9y`jMrSv!lkTfXLEz-(y6(Q!FzHo>63Z7p(#5q<`V>eq?kdqv6u2%`x zi{aU=R4L5Y8|_Y|=#C`qxB3MB?2B3&Pmn;AfHGX&V@x0!6~W7csK|%0o>N7hOtol3 zJw!!*n|0N$Cpq$|$S0uA2vy{_K}D3r94e9#TS`S7_cJQuDD!fqBKgpyaglD6){=J+ zlFq3m72|Crwty^NV6tZk>)nMpSa$;*X~*D1O0eqIi1rpLooFrw9bwU8Yd=nKOtLA% z>v5TF>QX%YO+1CU7aFYho}_azdSv+Ze9r2nD9g;J@~sWjlsGsC$=qMkDdQ%4009Z|nE!qh{2l0`~r;(%b8#;l}a4_e;?f^Jey+ArS(p`$CA5I0`eArWaYO`xt%3Qjg{AxS8)r)jw+2ziAUy~p$B zl2tG}-t2Z}n}trfx2?Q(TQVkAt{N?9ZVcFmlY|QkP57~@)o4ITII6C)uUB3U#|tu! zW`Xn_aC=<*?6zv~YfYz7oSlPZF1*|BRc~4h*7vC0&_cb@iH$oCCTk#w?0~9z zdoWpxQxQ!#4|!n$m~BIA$MP$2EoN6{ak*@korwp`05MUaR%o}`h1IRN*=TiUYw?~K z#CcdUUM|$s&t=Jx@xBU3k)P!(3N1^PmEyVXJhZoVZ=qEODME=>Tv&iBV{B>#`N95G z>L|0g4vXSYs+Wv)JG-aOn`&3!`*HS7akg{0P%qWu7K{LPwFpQ49WErVTUo|h=_SXf z?xdQiejBtfS<4sJb?xQF<{4+6b0&1azLTn*9Dz)t#h5CHAFdwrc zsC&s_alMzks0E>Yt2A4Rn*bf4Qvt~)YZa&%wBW)Pd4K;kFomR%wRCp{jxXGnEUn>X z(ABwQIVkpSkP(QpQZj(Fw$SY~U~XDoN)D?4R@aoe3(Z-DXBZWB0V3771+fy2`{C>g z(-ee`zSL~T-BM$A-vUDMhA5_fhp8X%8N<<3G4h>dj$bD+j0 zFgsgUCdY$PEX*qFpct5KHxZ!+;0)CL^-f{mY`HRDuEBp$pry42>fY-1LIE`W&Mugh zHpuKn>^J{8TWi2lIv<|ni{%3J2f)Lv0tlRm6KdblZ32lkl?7CRTBE>~8h{IS4bTJ6 zx+Sm#`=Oz79Cx7TL=j5eF&i%wDz#Y%!_a;+;z!~D;~2K%+HP(b4=at_F<3c((2;-*09glg3)(2bnV{emlB2Av^#Z(ffR~}Q-Rj43 zw8|xReFa|R-zTe|M34Om{8+gJK9=Ff$MEM<@X<}iuq?a}!CK^{53rI4Sg8Zuas+OP zH2_Zb{PLn?Iq$C0^YSur7TF1yh8@9Mq30{FjZQLIR%wG&g2ucwt%i>s_;Eje zJcJ+rj2}P3kJqk&k0O5jB7QuIA75GvA790fZyg06-(w%gz_%Z=4}4h+-#_anYfNM^ zA+IS`0N*%DAb5TSjhHOQN7KB*%Yi%q$qRFhngTm3dUWcdDm`YkI3}sGN=I4kgEMP& zdRrGK6X~|Z9f&Ew#1>&G9CQGVoGmtLHI7!W>nko3*WV7%dUNl3Q1xb^1x7=>;lc?j nOKV-m>Hm5gO&mk^s!F|B>y}WH<`LB6C3GahZepm^otyrDH%}io diff --git a/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/core/index.doctree b/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/core/index.doctree deleted file mode 100644 index a5c934a712598fcd9f6f75684627d3bc9d24d1cf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4171 zcmc&%TW=$`751(zS(0t}lDH@myAawm^`?>B1YMw;v_OH~hoajWi)7OPg@OS!Lk(GT zF6G6RTLfqyQmg?!C9@9!@_YKb`jMO&Nk))v`w{^HhKGmbIfvi*&fz~gFMoTm;eU3R z6tU4p$rY7~PU1q+%B{n)t><*%4EQhF zUsS=sZb@K{uGoR?N3H#A({^jKFYK1!Ijt_tjMxFuTJo9EaM!uF$rA-v>~<`OQV`<1 zlanlb^%7Gy17o)}N!_SoL-q#SVQ;ccc48NgKbU@^;Kh{8sK_|>VbhOjS*Yonx8v5^ z$AZJ)@v|GvihK%_RV0d-h$%6;ASGX4H*MEVU6iIgPv~MI6+AwDdf&|KzA72d7ZZ{s z^)+_9&Pw3JovUvNe~kZI_`mHuhb@wOf6wezNw}=+TUn79K|dU=d2si>>`#K) z_&c>&_d-|1urv1aWz;U%DXs&T-N)^bRFaadm}?(ChNmN*&m~b>nphhQ<031I9M-B4 z&@K*=7z3MU#AGNaV;VYdciZr@&BDK2##dOX^;1qVl{?0z9wwIAb zLQBo6r}sd&r1W(I_Q>95(Vz48N z0-6#CTU4xTcY+lWj=se7pdK12Fxta)M>7&%x>;D$aag&j?9m*Xt=wp zXyMwE>V+Mm`tTAU&sJ){ecKx(25x`3e6v7Lm@Op4=%*F@$BBs3%?863o`7DookpS+21o#%~%I?>}U+&WJyHxh{&di-H*uU*CY`R?9jm!7m zfA~I5U!S?N)E+nw06xVKAOVjmyC+DV8j@1PJHhxq$iTiPDN-Vdz#M=M&T*Z}O5^I= zR|uXo^EZFsXpLHAZ8;u7*w6w(jBJ_NZs05zOP;I%Y3N8VT8iv_4y^7aCM%;B&$tpV z;DEA3{dSnx_Y$8?4mV3tsi;YcXpuQ69?%7PLK>~E^!SQ(bj1}9P~VRduE3 zh1*S@A!m0H2!+h@NsGC?S}khm)`!M3WDR1_DKZeP6zr%`edJ&cGFw;sf!hMsp_TQF zKyznC0+j85V}Y?|c<7QGSj|&FU+|3U>W>FMv%M=KxJy)+H7scvAw$90d5jA~9yrO2 zz<-yR_J>UCQhjiK4zZe)z7w7lGCi;CvD5UnKr_-bb1Vrs^dKMq^cTOxoebqXzO%iP zEVqBV+1?SROPEV4C!FfJKLhbNXF3x&tgp!oIh?qv)U&6^h%9QUp>-fqr9gyjc9j?#_RxePT{&IY8W23IQTTKl{Bljiy)Y!e%i}g0{#T@W(PQvaWaU9U- zg<~uip{2+Woa1`^~9-(b3xeCDxfMH*b>#6c&8P6VC>XA7=V~MObimo N!!_-=PQ%P5{{ir$Ttxr? diff --git a/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/core/log_config/index.doctree b/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/core/log_config/index.doctree deleted file mode 100644 index 6fcf73d3245ca4ccd42f83db59351eaa16a9cc66..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27189 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 diff --git a/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/index.doctree b/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/index.doctree deleted file mode 100644 index 0d131c1cd6f034ca820bd25ff6fc69675cdf31b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3944 zcmb7H-)|%}63%WilbJ~}Np@L4uz?lgM|L3-SRE3}O82t7EsR$DddjNh>Gq6G-0jA8 zCzA)HI|+`Buz11zFZbv1x!v8ZZkv*by;C@mXOIqz`JHCmI3R%~%kvA;j-qM$+(WkQ8~nDD0*o z3%_z~%wA(#>~*%z?ppoi)01yBd_N&GDpC$R?Bq+DOMTLb@u-RMiQsVf=-Gu~GCP6G zIubG_;)E1NlAL$Xoiyi8c$U!RL}>(j@bqRev%5NHJX=mkl2q^3D|K3#4v)azK(Oo{ zQu`ME-^TyDK4`e1cp2)U-N*@7&c2z-q!9G+y^iy(2eNMjjiWzPVEw6aq+o~a{Z*Q# z>;U;^qkyFqHg;=)9eq{If@K{*aHk`xo*Neb84@2XlK&f-zudpk_{NtW7wgKmZbnoosZfqtB7k-v0kOr#+1)DnD=WRyin9k-cCMIU|Fz>_bak{J=N~=% z_#tlZ?p!%)_k08ZpJqsqKt#^&2$C%dvY^O!g7r;MfW56~EL9SLF903f4W98N zrGNvw@gjRixr4LqNqTg&EZFd>RTS>ps=pm%iaXM!^BrI z0?oV|2~f5LW(CIT<-RX+U^QC+`hut2xG(nJvx74txGz+=HO^@sp+dpbS&Rom9eBx% zAbww%_Ely~u0K6KhFDMXa$uZDwK#V6j@K-Gfo5cA>bVhc7(jmX&WHcNn~c>wzGB`> zR_5QW&3mr&1#?Q(gi|vw??66|nMnmM8yYf04JW=A4eS9bB8?hqXab0I?h$%v_w+?( z$TDJl!36$MJsU#$!rRs1*bc$rR|J}vUH z<)yv^ge0M&{fK|+fFAU{1T=7g*2*X~P`wjF>olS%;UdDM?D@OAe82i+0c^!Q5aXp? zr`ehRRIr0UN#Fp05_23!iJ8a{wV?z@7$)scpe2Z&*9-Yoi_ zmGRvH`KA|!--qm1_6z%&{aDzcmzs+lZu+_X*Gu;EWx?+G-@l+wKEfc|51uq7T#wXE z`(=-so?8z4J+3kExEE+n-)-JlTdTVMM%{Ta9{`ESVXPwq=p3cevxbNK4hKvlS~A*zmes}YyL*s3Nk0CC|lF-klS MSG40Njbb+W0~FH~q5uE@ diff --git a/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/migration/index.doctree b/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/migration/index.doctree deleted file mode 100644 index b564f5b39e610284b8fb8be35d5f032175af4c2f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3559 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 IA|fpQA9K}+8~^|S diff --git a/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/batch/index.doctree b/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/batch/index.doctree deleted file mode 100644 index 8ebda67db0b1edd3540c8674cba50731604f9c76..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44755 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 diff --git a/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/filters/index.doctree b/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/filters/index.doctree deleted file mode 100644 index 8e178a626fe50a57db533590ab62b941d0e30063..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 146068 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 BE2{ti diff --git a/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/index.doctree b/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/index.doctree deleted file mode 100644 index 73a9a8ac602b6190daeaab4febea4fc9a8abc3ba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10245 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 diff --git a/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/labels/index.doctree b/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/labels/index.doctree deleted file mode 100644 index d071310069ea0217f4f3798a3afc91a4e28a3d58..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36905 zcmeHQdypJQdDr9K(w!dYgpegz9Lw4R-O09Wz-LRrmU&t7Szv=*mgLpk?%d6c_LZGk z>&~(g88BF(i`aMrR7fzwK=>mS%HTLas8qlh@~V(fAt4nYV3MLLRY@oyR0Sx=@9W3B zwr6%`PeLSxsbLjdrVScGxPUC|h>5(lYCv;~i@i8+OgIXE*A8dTY=cvWAa$ zig-Eb*UMmAdVYh1@fZp*gQ(w5_7`(m^m zrWL9HyW%j3xgbPQfMOMV`4noVXH=kNGeV~(GbWar1Qzy-HWe%wS}_r~H&{EYDF}T; zaSeXQL=Osj#0K?%4Ih-c#Wj60xDN>5mW?p!-K`=iXJ8zJU&1ze#(cYKv`Xbhwd6L< za!6ij4L-orvW>^?K7I9>)7P9)e>+sidjeAIU6)+NXRoPa)Wr6p$!YSLc z=&?~>Y#WPap$rI{K^dfs@vK}yJI-%!Ds9(Od^k9OJl9M3-6LH0u?+ zzF1mnnF}HA*zFgXq#E6ZtF~vhjB1FM^@PNOi720)02Y2f85RK1oOiaSx!hqx&E;tb zs$(iNr{?)E+V(sKJuQy0A=DgWPlxT3!_MA_Q1|+sQBUfvPhy<$j#r9jzhhF$Mi zA7{h3^Ol>i8rBjUY#r{+rDoMG+g|5k{`!>Bs&~!-)t6_*Adz{bXCuhN$LG!*r|TK5 zMbqoBYa<*djY=^!;OO=^2JT->R6Dd9t@b|j$VqD`P=xn|6~WXtD782ZOkc#nL}k2} zvLv-WC4M&&JqAMWQTt@xFUEs)%vkD`g* zi6(kiw26u-ZFV34Fg^}tg-S{sAmj%WLAHNIQ&c%VzsoWgSP*h>u9Ufw)KQ@>M3F38 z+2@lJFn|I~Hhls%K=xn<^FbiIi=H|tW%Q@kv=Im^RBJc4Ljr%{e19EgiJ>~m*DJG)YpOz7lLxha+Gj^6Wc0jpo^!RiRHD)<~BDrqb}o5W(!lFkE2!_g4VNEF>+V?+S6DEL}~`5YUP zlP2pKU!{lG$U?hXpF%y>_tkAjE&nyB5phE zf*qd&tk>Q?wXO<=T6dRleHC4=HC33NF5GFE1p?_pW1-;T zn!gtAD?Um{7e@){VN!%jW`jJbl8U?w1af(1ffOj^c4r6vKoFB`O`#aVR(c$^+AMz> zBbybzQpKcMYg&eDyJ0rhf^)?rzYbV@MXcz=NvZQP!Qwv7qCQ!8E+VEj5r4_KU~1xg z9Zethc{sBum%CTU>1Qw4Ww=*DJ{7=2;4R?*Ko-^GVVSL-N)y>68!S9`_ObAg)g{(R zwlzT)0)fRd1&9|OyNJ0Pz}yQ`q(yM`d?C~=Jeji%e_{q$;Iw$H^3lQ_UNH+ryYBhg zt6p*UZJ~Jh;OlOg+EXxR7H9SpuD)vDKE9stD%feZYe~g+UK}FE##ITtaHT7;r-2X} z#CDcxgd-LEB(GqXV$03UeAXy}t zVyR5#rMDRwlJIIJ3i*7L+lNA~+2c=~=`v7|m*%ZlCP4x>l}4_m>ww-3dFj34A_;PT ze}Y^sJR|@?Tm{v7ecoybF-Cf_a|ab7l|5Yez5NwVJcg}OA{oJH%^XG4$D#ziukR))Q^F~i*XADVVwXPwzxQZZ+$zq<==(%F7W5S!9EQDY zI$sFAaXu3CoUVEBM^k%G(fKb=2u8 z-MigQ9AtJLN7aN!(fKN*jIp4q)+Y;?avKu)6!V`CSOO<}V zz2kg~%K1JuYt*Z_3$1-#$`~#FZ6u5K810$~74jU>3^FS`2`Wf9RTQl;>Ua3Ct5ohw zan=l=X2Wo$@~tV(TDs*v+wgt8F)g;VV7^*!sLKiUcv|dII>*+_m+F2KQb-@E>nGY! zZ>VP~9hF4%T+{Qg1k^c4b^LXzBTZG{KxxG8 zIO!Y-@9y+{uc|qFV`$8)Lgq#cOD{)*N-HFmIKZhZC{5zjF*BwX{9-t!PIY0b^0^e9 zPeJRIs1UXJPSekbe>IA$bIut5UB`dd^WSm)JHdZ9(CsD+^COF45aQFzsEa5OAQ_z3kMQK+Ki|_P{;jlOz zhsAAbMi%!}a+@+6R({Z@kqax|k8ByS@?+@~lCV-x&|qcUwe^|Q@)c5rZOxuwDM<8% zmQpa$VM^}>{8B38iNjQ)U}84-*HVk`OD>$kg%isjf9|s*D zr;uI;9h}WI5Iz@-9Y<2j&!9arNPt2J)K>lhPGbf76SZd)6I$!9*PtbM5d&-%Pem;`F@5@+Mnfo->p9D+7SpPU(abuld zT}os9HD04cE7HZOJ`Hqit+E;wI481(G(_V2W+eGUdz=hdY*y~=m$)pb$n=3ibOm0T zwPBAh!iGh~RnPYCgWmENvECAOwr6%yJnI+3t?_+)H{WLN;H6kvzBetl-;J#)P6@_z zF~L#z{0Az2yQ8HdwwAU`><_L-_Eb&i1f&~e_}XwFlJEF+(OLZp-+x{<^S8lG7gp83L3%0Z06yiD6mFVr7yYkZ!>GfqPLm1r}EuHESYTP-RTsPhOlJ8N0)=I zO)b7Z!S`)5YXz^jnO~F2cMrh_BX4+dFbWIbN`i$Tv-4+2lXuwO8XLA1j3y4$KCsH6 znQhaT0K|(8*!uu$pOgJoJ79-5J--NnCfNGq6|jR!ZJE0Dj@##G^I*;JWIXpI>zOV9 zyM@#?Qo04E{q4-xf#b5w1(*-EKaRd?$*3XHeAZvK8&OX}2Bn*u;LD=~S79rph-fa& zR~zM%;cc{^KXBlH*c@EgUx+qAkuUD>lYRu2C2JPb1 z*y*tX{_TEcgMZ+G7F2d4SmZXl+-kUu1#jk-W$eec%kIp!KGSAMlJ+f`b3J$YAw5DAvI*ybu@ih?y& z?jk>gT@`}!Cw$KNRll5{phdzJt_;46D}5h9X1PLLquV7o-yz>}ZH8ii+$%kfC9pMm7u~S?1h-l};haU=bAb znjG!Ia+SI0*LsC@hGq;aqZUA#3tLlwbE5(An}u=d<9(`5b02o9-7uyY0y=85zA&Lj5sDg?; zaTBog%2-iRP!2bij9=N$S<)v9I}h@PVcm>!HJh7d&*cGELK>0EC2kVGD{sA|s6P-gh9x(j)^C})(ea|aS~=f3|@pw#Ff3(5PCZqlyg2lCcSN;kGEnVv@U zShFixljg;}du-4I@%+K?5;L}W*xW8~l(co91`ruGcR5d%4ng;{@eM=q9YXr-nZTh? zq)`-ZVv$CacUlD`hQ4ve!Au)Sw?#)z^?LowKu8~sHH5x5?eDnotNIyMUvy0zU0Uf zLn0r|Td_=nq*W;;$mkl_`MmVre~|?FL|=kT3|su;y!F!IM$+K^On-&PVzQ#D=M6Ma+6q;iivzt4;w_rM3QqSLrmoQ9;L~cNae#?sTcpXaraH; zEj0W?is8tftea;j<4l}vZ1YUKdc7U}-R>p=npjxRx>0Dl@Z)x;4{XqJN|xxL&%9}7 z)Gvm6$B)3f8d-tZqVvRYe9X3eeWzc(&iS3V{{8}F%eap4l5`45>j;8E+!;rE`M3uz z5cJDa%U4dfh^5vm)<=-YyxlYGR+EB>ZkyC^#kn<=@gAbr2qr#X4McQTDy6g@D_`sB#wxW>L@qAk(_b+sdq-{I zd=OM=sucPt-Vw~PhNMKa>n**)S5;;O{Xr!|Yv8)@Rj~~I2xyDNFN5=|eixHD zRqp)h#^UzCxC*FO07{1AD!1`9+p1j z7sH38JLAJr5Lp%T%;p%HKIzlQH8eeiY#E29XVWPp4NZbVE(HE^Dy2(@z&V8VZ>d#C zj3bZ~q64Bn_T+o%6q4cya@65x=@fD|cs#CP{aBjOm!+BS#Xdi7tMnOq61GKzV4~2j zc+d`hy?S-t(34`SI1*Ln3S>hMj;f44^yIp93P~y>DP-9Ob8|X{B$W{qG@&Q-B0Mix zQS$_uoyVctd2N&J!Zy(kUUBkp+qBU={TnGaI*X^AyZ(dyp__feENLW zFNWJG_axgX(LfL}3CT8v?)Wrv*(r}8TShzOd^&|B3=$MHc1k<~{0~#hS3^d=DKrn5 z>1$Rf1rvpT{S^AMsf;Ji3KInr^UVKEYVm!^C0{g4cr@3FMIX)btyI2yh$YkJ(Em)Q zkXs{T5#T>gExtd&=Za?ODR{k?Vq#qyYF!AyUzScGUGOoJN^gZ1(&m{ixL zQ%J9a4$fi`;5Vn1pGA8vq!lg%6E$8G4_Yg{*O-;p3coj19En!=J;;{P3O|`nAxUK< zg)AzwlujW@WdsF{6;3b0=EaJdC&=ua25{xI!Y>n6xZXO8o8bwDld{7fqehF`;W#SQ zIV173FFRai?$ZwcAXt)tCv?T_@F(c%Qrh7Mf+*>%-(%uyReqHBzC4DviVchgq6F(x zWSd^9;Oselgt8J^@YpN4PT72H-vdxiuv|7cQ~i(CxqXIn|l#3YmXi z#_r#On=X{q8@u00WjxWi5EL}SX3W_AL2B`R$)$f|S1T61u{*pzO@VueC6lqcC7nXj z;FB!)=(d#?rxxFz;QKaqwSw0hyVs`j-9zxfm>J$V9<_&L=yo>{1eu*Dp|yDj+;K7B zDpZaSv0Z`M8zSku_=wEaM|}2)+ypBT zg3g18o>D`@-3JjE<;PT0Og4?Q#rSgQ{#dwCF%+fE0u=&->ENp3&_U1hn zKpD+Y6rmydpmYkn5}d!&=bT^l%lQdlN%uU3Z4k#3`d`Gx*WdA#ZbC-&3WqMq^H~i# zwUWRxe$XpURt>6Fw615nnvGLtt64QX`nJY2efj}k-f+!nP&2FUbd}J=#U}(5t!RcB zKf{d3AUC%tWUdEw)V8?OPgkcC-&ypi2O>p+hLn3jdI(w ztM1GKj?=@*wv`SWdJ{il$^X~}Ti0lN&9;XlA3duxXHCqp(M7XvwhXV)LUE%IfQh)! zS`#E^cLqCzME}cKz1DG zYK9&^%*I_DQ(14v)O3j^g{DKoT7bb4I8tJs((AkHknH{M2K@`+pbD9$ObFse0ve)O`I}X zb$n=@jv+LS8lH@zEy^ZUhWNAVBrvm%)8XiI>}<5{Elj^+njgKnBl@z01Fjmal7$nm zs%8t~Csxb&Nc9FO_MntOUP_1UjJ=5!;d}wTkWGkkgWN;q=GE6+cTI=wh`x#CW*eyz z&^*_IjH>+b<=7{r*&4InVb5!sWt^&8s+dja4d~$1unvy#RT{!+Myibn7;U&_DV&q5SlKJxXS=r0^9}g2oNLTi zP3WEEa#(dTblCL}go}^wi?c1Daxz(os_CMBTr{5Tp#Y)B=TL)2*{&~u52@IjNz>i- zd^FisDgxZWz~FFz6Kugo1&X$=Wt@VVsoPD|!=bJ?YuiGXtJz-XwvD^l2o6oNsh6Uz z6HT*OLiaR*5eVeQllAmu-a!4SSF&xE=QZ8gy?gQ4ooO!PdwBJkMr(0zhfPvS&I?kO zaIhzVCO@0oY)RPnPL~EfOE1sSN#b2q4C)lJubJA?h*rvC@X+R!(Ew5Guu~8fs zj&585<{TPfJJBg>B_AF93dkdo&#{f}a@{kQN|wE7Rq-DkXn+nK@38f54QJJseyt5y zaUrwk@q2MAVo^o&>?~XOeq9;LBRV_;&c%t`C)>>qTFb5(ix4l4t>c{z*yv(jN$ervDzpi@0VVU$w@?Po#G@gBZH_#pBV+kvl(yjC3=t$(`14_UD6Wgn z@6m|;JM?oe23_YU{k)rQAHz?ZjZ#{EvILDiJoP!A@*Gcv53U#JITe88)UU^}uS0Un zi|Xgrpgb4V|MuVY{;*9WBpb9{44j>Z$&Q*Fr-7f3k^}01cFe_yE9+Ly5cOSFKh7apAKyh<(n-W+AYH!z7Bcp z`ApQIxoVq@ht-WsZoF)Zlg3HK#S=m%l}-@)DUDwHT9??A(3GBuyG&)EyB;%(&Kl8ty=ZLug diff --git a/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/protocol/index.doctree b/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/protocol/index.doctree deleted file mode 100644 index f122ec2c9762e6eab91ff174bf87bc30ee386758..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18057 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 diff --git a/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/record/index.doctree b/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/record/index.doctree deleted file mode 100644 index 13d26ca85d649be13c6a9165e091fe05a605dfab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 53127 zcmdUY3zTHVdEP!|X6Lc*)nGh4d|3kYZgyq?ff2J0-bl;BfEI*=*iBDQ_nql`x2L<+ zeS2refW5K~gtf18FtkV<$qtew6FXLHh?LlIYzMEfl*qPn96ck;QHT=RIysI`B(M+G zk@NlaxK(xg-nzGYHlT10v$yZ9s{gOQ{#X6=SJj!}4?g<&b?m=zYo$@{1YWH@Q*Tt< zb~hZQ7t3xv=swfk^ZxE*-9k9lDy_5|omScHhU-wGT=i;|mRs+h=vLQ-o7!$U@EY}~ ze08`wQr&c-TfoQRzzb@w_9?vLzQ&5%y02CW78|YQncGW2>6F`QyEDsx4>VeCxzVb0 zsR7g!oG>G+OC2<&RFdDJU(`uWof~b*eg1y{NjedU5#v z+h&iytBrmgFDxDgc>2E8pxUS(2g>bYtx+!3j+Z(? zqtx`wddH)B$2r}ObGjY(>J|6&OskEqUvhLqXCd6yZdSee>6uccB1U+amC?q84_Eb4 zbiL|;?7Q**-^2e`5($7*%VQlcghS1e*XoAT0NJUz2OOg-sY4m=I7o zTB!bgMbNqGB^W6%#25y)wA3m!t6B%bJ;xZ=I2CY{!wBMlyPSwf4tn7Q=*A3ZtjT_A z2%Z8mhxq7nWJ-wyBfJk(FRtzZ`9~%GzbZVtmvD~N?+fBEuQO-8>L#%AL{rGzxx#qD)u6<1Y+oe9fSAhzj ze$$zK_S&mw50ap?JIl+Z*6Lw!KlmlQKrla)$c*;68}8746+s_UM?F1`|2RvHkKZ4( z8ug{x>U6N$bSoN9afx$QyrAk-ymByI^G>?XLaBUmrPQi8<;HTe6nG0>%?nm%#>d~; zaa*g$+;*oHjE^4)+!jG~R=~yS8kN;V z1UI%CD~TV5N&ocLsUDQ5gg4JNS7$+{*?B>-gSqs~3@?3n{y0+!1qjOKrdzLg^`+wJ zmb<876W+8&5Yn1$0xMwLYg*gYUlvk7!tv=Up?^MI=%I=kj^T#pYBwBdu1oZOVm=~2zm$`(+%G$%*%d*X!|40xu2Vt{Vk?o_jA($kASK#m%Mtn`eL{V zzeqT`;YLCuoWO7QbhGA_y`cLf`wWSwch5q5ug-E~klUh%qo~8|aCeTu4N9#gH|U1@ z42;vPj~HgAv;G+j^AD%4I=UXC^&v>uWOYO_z=w1Oa1#fTMw-mcXJBrqjekN_(ng&M zKZ`=o0MI{{@*_l9$i30;oX{}P^yClH`JqsKAwxyJ zo+d1jimd)SUETSk>({}a=GA!BKd7FoetNF@a(GQXb)|QaxcG-$XT28JS+D7Xk*>q3LPLwQci-C%9 zwEtGBN;BNau*dHE*T>p5Rb0fVw0IOQ@w zriyxUng?QIqN6ZXF^iI%$Lp^!r6;t<(1Hk6L=#2J40YQ*Y{Ku{)~E;Y$J^4j_qe9Hd>{=sN#6hVlBcwR;ytHa@XM=~6q6u@&C09Qg&fcnG#2z0f%y^*hLNM}2MW;3PD06kGu?L@Q6}0HZ`I;Vy!8l*d zDSjK=FpJ0Q` zli}rgsGtTmjct{^qWlDlt28sJH0AygM-4tx`Dv=NH_Wr5UjE zk_QLDt&DS4Y43CHf9KuK`w&USTE-s9(=eVQuvkQU@HI7z!VR8SOq*nnii8WfNjT3* zcpU#QM%&;NG;XEWDbt|MavB7*<~iF(g5h_l9ycKy@m*_iCDx+J|9K`rZHB5@Ps&Z& z#Qy!L)BgmBTf&!Nc+dqB7EIWgpKuwNE~41_JzDpA2sRlGJFlF1M)z$J%Dhu}wdaw2 z6f?x(*_6SQ|B!{eWE$U1132YBz>4|r!9Ou)UfG1}*}O=}$OCLuq+PDM%O!`@gVSk4 z9^k_VRTl;Wyx$H4$kG|OH?nB-Ot5ZG%?G=?!V7aHS1-u`yZpud1Uv?G2Riy40i7!SOU8zl({k|C#VsQ zH)7}E=PfkrlZUTk%gzN_1Jw0AvwqyQDYYGVD$}P*wGJE^ETW4Ay@JWYL)4IT5BLw& zz1OX5B8OXJ;MZ6Wdl;Brr!epqsjsOkJqmlv#)yN5sDP%@w$Z0l;mX6p6KEkd6e3rv zVxia{MV-M}nD+tnid6O$;Ngg91`qepEP;m;%3L8-b_OS?F^;!kXW{RjKNenek;cNb zu!FpbxklY(oP5UvcuScSjb`92lRI68H(f7xT9{y#SNCZi%VMjsJbrV7ICzFNCRFw< z_Z^A+{6xJyM5QC9XFn7{?zA#i0@L5h_^5~J>9x+FdA0UKR4-9$KOAe()V_IG{@n~! zv!3ixnv>$9&fqNH)XuLYb@kWh@&?kxLnFo1fP> za9(Jz$#6n@tF`pFbqM89kcOanRzf!>rC(w)xnkdV9lJc=WMPONla$(&e~c9Tlz(rA zcDRQZqlu9oK#ooR{2O_(AJ1s*I$li9`>Pj=AgJ>6#_l*>T5i_d*;y&oD3}>f$pR}7y3<}#o!!r$}HPXBpbG8kWf(AjTi!QaDv_YXdX>Z zMX$2YX}dKf;2e5*H#wubPwW|QV#XE}cuO;0Wsej6cex(B;1LOB*GVk zSn|*wNvZ$;5a%+0p*e>EZKtH6m{G`=Xz2OmX`eZ!eXj-hA4W7TJV2B0eDPResG~ek ztOb}EF&X(KV1%x@93WXzN`n6ppkz*i3jVKX@1mJ{!T$^Gwf_o{g_|In%PZybO{=IB z{O_U_|9kk867?0K)o(=2uzBk>ody5bHTZmHO9;?n#9LT27~$1ovr|Wuu4hGJeI(s@ zQn;lqlf;9RM2nOUxLv^;OEu^A^3bnIMHQwMu{@&6WolraB*Soj=cEXE%G zI@sWFjH@zINd9^<%I8A4qR2GNKkWJ&@!Br~XL2gEWsGYe5ML+BgB)R`hEYt8lqKPl z8W|!$s+YM*|p?jA4_#Ba&G>mXJq=z%0 zaXKslUjy5xYx}7=!^2@~HBrN&8L|eZm3o>)fLke1NQp<6zNPBs+=l9@s2OIcZb&y& zF(s31l0FtSmt>OsG$r#Ks^zpvFW4cZDH#qS+a~>8cQp=o_@Ry6CD zM9rS!r~r4&d|U^z3iA<(PQ-ahA+`l?8Y%4JCI zj9Tu?klc!DIStAEb_i*jfkQ|zBtz6!&3~P@wlCGVKD)`ZBpk#*(;Gn;bE5EYG*Nh1 zL%eN8Jk^G$5{Avml;d*7+WbIt8GP24NS1vOS@vmUdB%!&23c~=0iUx&=$j)qLgus% ze8SrNV1y?K=Z_z`f`HXse?r6i-&^s{AiOfNdJHzvdFoYWsJ_`6S|h~G0%%}9)f+|FZw=^eU_D_DN)_Ym^pI${67Jk+vji56SL3k=}ooIw3fAo0Azpm znSiZ11gX~IV)ps_G`M_v$IB=s`2#WTa_Gzjt=zM_*P~{beZD1bpSRqLCCEm-R3jVR zk9poYV)GwTWpW(Va+>Fh>=4qFA%~D)o?}+1U~PUdWk}gd11ZBk$a27ncj}x>Krqe! zqmi9^R8zO_utP|jll9SuciAEIJ+wo#w667F!P66$+6|o4k3+a1cW^E_+PL?NaGm}0Ukx1N9cn- z0b0}WsLKAU(71#83C!B``U&i{^bJR(@{W@bhS~zEdJ_#=Kr+No$4fz-NjzA>{mqsRF#Tj&+jJ$ujSL6wOR#6xhFUyhn#uEg7s6|gtl z$yP4u^ca?39?Mw|FR}KA_aoY+6t$u!7mUglqQY$R4Q=u&#`D}aqO7!N>{-#^wFvp7 zXhOKY9$P7;;#d;IKRpUCIqp5Q$y6sMkyHI40U?h9++l~1rT}Fh&=tAtq!ypE-K5=C zsM3n{6~q{PvVJL$iKjFA>+#ZDx6}vERAmxh)9U^eukdX?+W%Xp)UE_E6yo=)UT%{p zC)qFhtyu6jDsW7;1N4ji}e*?7do8!t`LqO1Lp_6chzQ++f}TIkRnQ4UE1Cm)r9 zMoU+tDJcOTv;qy+H%zto3>EK-VSd>PAgfn6e#xt|1Ld^cOv?F9@akT)!lR9w%&Hby z&baDaxU1c1HXE&=*v3Iu!TUN5I<$^0FZ`z7=f1T`R)z3Kmto{RY>2A6v_F!YQNBOw ztnm?hm~iG5j#0u1Sj(mEljH;b1?RFv%gp!Ewp{G_tG9Jl=tJ=Svz3}}u;6_j$i$Vu zM8VYOf6Rxhmn>>D{67Wh`gPOA;bqL->+#Z=(nPnql*;doU6COK6QqA~qC6B6HFSqozU> zb|Rt0KLM9UEpAEZcft%|R2I{Ow;UmxG{2I1pf49qff*?1Rs=>pbGPL6&Qn(8Q|E3R zLIQzek>(Frn;(cagHNFn$&$S6_N*1}46-CENs}#wo$h5zRoTB8Nc3x`<+2--%N|3` zWmj72En>NNz)T6bo61y6W6SbKs5!nbOEbgT7o{yeWu>y@4&A!k z3#49*kV?0>!n#QO2eK~z5pcM5`5Zkl>%yMiRO@n>xkGEPFK_KDyvL!f-NIZHp$!}J zAnjV^Rt9rXx%6gOnGp$?+Zp*&@y;t&-pOrg{yb`iS(^LTZfO!)G%U@w#NH>U(s5N# zvo_z45I%3#Mh7&Iz4;s9l+)g98?!mhsfv|DNFY)y&EsNg^HQVqWs4bv^GB9sd$Zq) zcLrIM$Sf!!1vRo|xrfPj9N_IYdX( ztikZ~x4nP?Tf5!4R3Z(tbA<4ha~mTm(=a{50yMWg;+JBbN35)q+wiPJ%`n6B(Ao`8 zLW_ps*_GP=1id;I71nIe2P2%%o9)ry45VX!9w_BBJ^$Q_eX5S-5E4ifo9cYZ+PqXL z{h6MDlWG#llAPN4Su5TdWXY9+@kKj?w3U-|!kbGZuUMNOjPL_5kt7m6+2+4$#XE!W z%4}!a{9e7FD*G)U(XY*y5niq*drZF2n9|~J6&9bB?lt%i!L9~^5?Ka+r*80Bdy&0Q z`lNLBTgGi(r(_Y`+&>Joc8FRz%)RWvK<0iA;Ba%lm!6opXHRdcxxbgrUn%0AprSlf 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 diff --git a/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/relationship/index.doctree b/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/models/relationship/index.doctree deleted file mode 100644 index efcfc3a688b4a72e29894e9a861abfee409134e8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 161553 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 U`X3Winv}+psWEa}lC}E(0C74Fv;Y7A diff --git a/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/operations/batch/index.doctree b/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/operations/batch/index.doctree deleted file mode 100644 index 7021845c0c14d84701a7f9323f5d8c5f7b5d09ff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 324887 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 PzABd7XG;jp3mE)=@YeZ- diff --git a/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/operations/dataframe/index.doctree b/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/operations/dataframe/index.doctree deleted file mode 100644 index 406df4d6fcc0315cbf8205f086905dec55037ee8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 90271 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 diff --git a/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/operations/files/index.doctree b/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/operations/files/index.doctree deleted file mode 100644 index a365a4050c079ba3ebbac147455d2a2570ddbbdb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30164 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 N2TJ8d&PsQs^8c>Gnz;Y~ diff --git a/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/operations/index.doctree b/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/operations/index.doctree deleted file mode 100644 index c74029a5cf4dcee01d270ba55d6d7b913116496d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4419 zcmd5te8I6K@j9Y0%5=rhVFd>bA-vSD;WZpk^qMHD@>> zIkwayZPOyf8sJ-|K!7|1$nWWo>POB+k`V;!JjH+k@yy}jIfvi*&fz~>|9x<{;s0zu zmQilF(8E;5R2R161PM(|@nv!U^WwAO)OHoQ)G}8QE$jv~BF06mXj*(yunoJdX=J!e z8-3PdZMOYMaf**E!;PRTpX|M7@{+1&f|#jP$?yp=ybD_#`(7-G#czV~w!vb|i?_ z5aM?$B}w_K6X#jI$nCZvGq);OpS{9%*d4aX?%KaUel-42Bcd^xP?2!hVdM8`CiQqN z@<9{%W5MC@;Mu}3nU3MI4uy<}7?a#clJWI@<7VG@nXxfXV>%xyjkr&q-p(g>UuTS` z^C5}j>S9}7XXPr&TVg*$+$=!C@8SRF_najBr)h*Ap4%g1!^1#qr%Y zXz!QGgBQ?c?U5-+&Q94I%RG+Q2`&gA=;A)fOp%POxNhHgw?<3gFC-%o)d3I{7)FC8 zMZx<|o(@Ym9p7wE1t&Q4`|@H-hsbxP%lZBeyUXq)`JFljc>lYyfLP1^>jTDhQ063v zWNHXcwfD8T#AP5s1wMQyCXi}IQvO%kLo7j~tplDK83;M!fJ!h^GSBp*K+#C5Sic#3 znp3rSGazXk7%~x5H);4WxU$UdA8wZ2-b!|Rb#`mI*{=n*_kKPplU>wkf=qmSpPr|0g2pRJY5S%c-X<=vl^ zrMy<9Bm0{h7?=?`$2`AY9sPw>`bAAyPMoYf`idXQx|NW#tRXV`=D=J+${S*YvZh z=?@%&eA6E+FadxurV5l#K@_(A z5fM51YBOBz$lPSP2{Qtyj97nUyEB@)QAjEjW*5;@ZAg+~v7@AfrUK^X1#8=#(u)vB zpW}0{TINb%wTG(@CnP#|U*S#5;bu|TqbZD}($JNU&+VR@;()tw?#8m>x$SEV09go% zbHkv9!8@gd3hE`{B{wXr=Omn0ZKwvPk=<38dXxzTy`aeGZQEl+G|QGfq;rl$8e(!x zJf%|0wqiQTXE?5ZNmPnNLPJrKK+?snMbw4l{Nt%h8%-k_L#wde+)M`#2Abpd-Sv+l zRZ6Omp&JU+0FZ##B2(D?D)~z*EpMf;C)d7QGr|66`=!z4)^?Qr^6g*0jnmh^T-$07 zTm%50W=N1gM1|cGB%S4CMv?Cr>)Rj(`%+0*Ay@>^!8vXoTN&8>*bc#y`upOK90Op3 ztS!e(NE^moh*2#Q+s5cIMS_q^9ic2LwVPFtgv!h9y3SCuyGVpa zW%;6o(Y;D7tO?ks#a?SOBA zv3hyviX2!?XMn!o2{*-`4qmgJ3nI8HRJhg8XcnSE!QUxnQb=~4WI_A;1_TG3U|_1)99LcCs~?*wKnf)(-q7) zRYOkA)SrQToH3IK9M(lgLk-8SLv`#4Dk2FRYG?uowHvL`c@FdfQ|mlre8vR+Q9WBi zy29Jl2|<~EnuAj`a`uv6m&@`c12NtdClSLa9|1gNwIks^o!EVzXTA|8t^|Z6WktJp z+Ia!=pc}@Zfd%FUMyY}7aRjaNkS2tS5N{mE-~RB!>NN`3ifKuVS9X)87w)}hJ0&H7 z0{}`4Q5+>^LPOMyg5u!gA+Q0E8Ppa!1Dd<*3ifDCTD|}whP7$4XggNM;}P<0CoF%T zvTxZp>}&Q_Zo5uu78%@hFRpK|*w9JE280T`6G%Z!qFNL0G-3~oooXHf5N8e(z1TgfD%x?AMm`z- E9|P6E!TdN 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 JR&#dj{{n+8nbQCO diff --git a/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/operations/records/index.doctree b/docs_local/_build/.doctrees/autoapi/PowerPlatform/Dataverse/operations/records/index.doctree deleted file mode 100644 index 3a2608c1e6b5fac5bf09160058f832116f324967..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 201405 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 diff --git a/docs_local/_build/.doctrees/environment.pickle b/docs_local/_build/.doctrees/environment.pickle deleted file mode 100644 index 4ab681e8237a044f0fdc22fa0b7da7e7ef8c5c40..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2482769 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%0AwQo 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

CVfgkYDDM1GU>Ax>Bxc0(P^FS_nu-Wm2n1> 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 z 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

jLL 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 W+}Y%OrpG6UM&?x|78K=L#R{sLv4 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 z zO2m>b+RrWfLW7Fo;FA!&bQ~8R)B+@v!f-KQ7Nbmh9AOsc|H&+Ilx&VjUFQc~!&p2e z3O*%iP0gj@AyHJ2rLb@Nint7p0&ss!)VV{As3@|0&R=@2X6fzPkcl`kp|S zzBkaN$BT67H$ay>HU$JA;nN@jDs1tT!N?_jx#6r?5}N_P3*D$FBNBB){2ns~*U4+PlWWe{#<;$_QPYEmjS z6M)rV#~_+>mR@E?Q#{?b0^PECNCHg85yw6buH(~05GkF6mG-tJUt-VP3X>#SaQkLM zMH)Dz2fN;SnAHEV(Q}PQEa*7y$S!Q(nOSvrH8jb6K|`{BKr?v6!g=g(G^T)h#~`H!F)S#I-~raNk}zh zvK5WbPWOs?GIH4UE!hxnWXaW)j*hql-*!1U<}anh7)C!(4&0AY<$^?|kd?j|+p>(V zwzj(4V#UUpzp>G7CZX1&>nN7szn$u&KOH&3#i*EtN(a?#H1!mqd#Mu?;s|SzTK%g) zt=`j1t(p=vdTAR(A*ExCZPd{n|H25u;yp`coB#j?w8q z!KY>Dx5c5K+356#1JU~#E~XGqSl*oDXDe*-JD>9N`?;{mpXaBW5uN_N3JZ$WLVqDF z=obqMy7Wb`3{GoBMXIKdx;7AT6iPUVjUtdE)m%aE-^@6u#YNg}AzH#y?joMZAn@71 zd3MxfFZF~P=^^RgvY>X92N}^Kb@IMMnuXFJ>CiOwP+p*PB4bMch4Np#y1qW5qpR{v z@uQ@6Wlwn$PU<6nf{VRyv+Sy8<*)3l!ghCgEpf5MpK%&m(JXjI^#UMYZ6a(%qY{(& zdkUzbDv652x&RT(F;y_FNr0NUz{2X}${<5fx~4i2u3pzA71`oq`LqyHeM6nOEJ&l! zft5z-6OVAGGCe*g;c@dY`NhP)6z~`iqkg#XDz|Y3Kw6siXRuyaw8y?tB#uI&K~-HL zEqdhAA`8>-Pj@L%E*5u5r%o)C{pTK3t@QC|p)0Ecp}$&z zslrmVxd~U>MSl~QsE2YCDtXwu72vOs2uXXSU*#vIz)yU!*nzUQ&(Owu3fIX}(+JOP z?_Rm`mv{oJh{#zV+50)2;l74_s3txJBfiGS92~bF@crgTp zw4OlJqLDFxNLWUKAbk-rRc-ihM^4mhM~++s4y`wj9HFfrPH(Avnu3|s34?1Z-sQRG zb$}WI8Sdcwq4Zk+AuhL8POyH)9alIE^E!6A&;w7%Adq(?%hW==lG<%3!h~Zdp`d$y zw>EpKK5{3gywE`E&q}rVE-zoh+{@SQxWW}DlZp4kDvO1QSS_{pICasN; z7+CX+CCQ>k31WmUSr~lv=-Y?9#2wR+>WdU-U&Fxbwd_J7;>;BNLow+@CzX73NAatf_@ZQ zk;05WP8|!ncQ2|K!IBiv$I>!1(7$8JW{}~4GsDi%i%HNQR#<)q$TTvdh*4=!xjZTx z!R#B!b}~zNEGhIuO}sXR+S4gb;CClSA2++}FlK{?QkpC9`am0?sN`0tFZnP?M}Qpt zJ*UY?0VyHWpwzdpOZ8iVy#0HT+V4TU|DXc#j%EM;_dvY=ke?EHTYAq~d7v`lE!uio zcSX)43b+CGgS>$A0MTZ~8fyuXyd%7iLo34$@~gbbuwfO2;PHjC7uaa(D8G*t-N0@s z*w&!6Z|`hL^EXPdp?2N~aF}TClM4OR31LWqQAH|O!H|Z$9jN@2%rF`_N?!brmcIX5 z>1AOnrPVhqj5xV8Qb|B-Gf=?&Ew~B-Fp_9p+j=ZRRXfQsUhoRvWG8P&5uJxv`q3YfCbyU|bO0P$boc4ZhBElN= zbFnaqh*NM8wv37$7E_?{apakZlg=6vv3)!GWpq&3mm<+R>bhT*VowxBK^>({TS9N8 zg(s#%J*DM`@unjL1soWj-z5Egg8w^jtAub~F*z>nYTPJ1lKMd-81yq8s|&1k#1K;H z;YKkC3G|46RQvb_P=61PTG;1&`MJP|maUPkt!aJ0v+M+w62#4RWY;G zy`~3E?*u$oPUZt-=N>sCn~-4>^cCb|6`EjkDhY@Z%Z$Zjs!3-$%_(g#rW`)ncsn5; zGpx#|Bj?iwO@;Mnxq)OonrnoBK>dZT;+XC(Q5oSzC}=j{QafZuk3(97D_bKBhB{6% zit2AN3h1VVu7st-d73sr)b7}LPGkGhdyo<6LK2IYIdY|R#o#M2EKl~#(;*T_Ys9!+ z&<6%w*nZU(4~n$RQtE01LM%BL7{0l}@RYp6Bd$l@?0ZE6wkDY%$+p6h``#m{Rk9&( z(kRUYQE6;riJ4`Nt5X8D@3Kn zLWp9Oq0555Lk)RlfR`a~c(s4|A=Z}AdPV&XY7O9SO7lrI+#wM^$-%=gp(m$Ni)6Zf z%2=t??W&Z%->XVd8kZ>u!O`U8Hhz)KL$I&JrGVX~Z8PO%z1BXSb6r>QmW$ejFO+hl z5KagS1;bvSwT9)ZY|-0qGdwVGBhkzjNb2NW$MxGJJb>%7!^8^tX!O+c49gFpx;i?Q zDwpBb&GQj7gveF%I&)EmZ=OfGs)2UyfeU?i>% zwBX(;z~kmR8emCgKH<|zd^b%$3$VoOvk-piqawgEgx--}aYiB2w2K+M2fMwt&lyS5;%n@~{VtA}!VMq(szq@s}eC@@@ z`?Epy{EEXWiO*LUK$q%X^(*qq5;&L~6{|sg8Zm}DZPXE|BY_13YPbI2iTY{Ssp#ll zkz|Tc6quY7hiF@bP|D+^r6e*})BYgB=u?{F*|WA{{J-*Jds*M{6o65byQ|KazxBY3 z8GatoZW9hx!Y4~kGi(54KB1dMn1oX3++q=E_P4gVrK&v`^F*{d*m~ni}lEN ziR%Qk8pcUl&W1z@G11ORr*pces7 zi?uq~YRv{>U{jJvCBLYt15`K&-)U|W6_;E!bQY$2jF3@NQ=Qq@04C(%kOivlL0|0F zPm9fLCRq8H03a;EC&bVkxA+{tyD_HCiY&HA=nOoz+;E2@-rHzrdktgv<$q%OI@@@( zwXw4SyVW+l5rYhCHmP^m*K+lmS!aX(*HU2Gag zaJe+!tMWT7cm3yb64_HNKc$CHT#4$uYe!-95-BH8*g+2oxeO0H`%DHRe|>$Bzt$_u zU+oI=SBKAt6=5^TU;m^if32egF^Y?|AjOEaR=nQ_ASe;k z3kFLG{j{33M01!EB)S~^cC}a`3SqadLbeju@<!nLN{rpIGUrw4S_ zYY_ZD^fgZ+HBWQ^9kV$ii3f+Nfz;7 zaVSXfljygpqb3dxSQJ^JvQJ&f@A&jvAWq=l+DE{PDioOfl=uM!zFdY$^cz^UEReKB zG)>-zp5bzqxq03d#E*AY7C+uqLHzi+=eYRsZ9)9_rlR<9c7*9K2(-{JX8?AdAn0Aa zk^c`f3;W2lQ7oR7Gnu(%yaW=H3wBynL1CHzNX zqlIi1q8J?o56qp3!PmvH-i=Li2T!c;XR8(VO> zXG4Qe5c+4K+||`O-Ai330>#;2nQCkm7E`Pq>1>QvT=dGwqhWswI7P%*iS-KBBh@MR z1y8p&wuI_Il0MTtn0to2u2R4qK+}aXxzP!lgT}R)pGzlI>1fHBh=mwFDmI6PT#0yh z1}6@AEH3`o0!8|X2amQW95hXAbPIoA31Z?3F8!V$YJ)8^U^gJbrbi&04`%L0d)G@u zBR^IseftK$4QBJ~AMRu5vnfxC)bokBr+|h)7b*pgAOg3G!H16mQnkT)q}vhV>1(az z0z*_lNMpuhhGTp++8GEX8HP+g=KNF5b1ZW*H9b9k{P+nZ$^<6M_jF@;LBbsHoJBI$ z&TOwR$d93YTdhq-mT;5{RtDID*wfK890F13XvZkTF%Yio@Epf^S$=dw>_1i%;pz*7 z0m-G6H}159?;LpVcBskb1AW!acRvL>gFq+39ZMWxR%ym%8)IFk9Gq;8z`%hzkua}S(KiWxy{oGLAeWb~2YITtj6 z1V=38H;|?#`QM&rCKczI*fA1oB*8ESad)MK#1ke#(pl5E&$GgBV(>@Jn0YlK=9 z_Ayw=0D6m`M^82pdAngBf@Ny#jZMA!mPt-73LB7)CEsv~pY=HQ{`9qGAx+z9U zKXp{AA&?E+BMQK>(o97m9dckovyL9p^mMHCrtDD>+OfJ+|3GY97MO&}7bv zguqWMU$_My+#Gg4Mt5L+x_;Hrw~>3tC9|Q)=tgs+LtDU@{w5+gTI9%{RCdEdaHu8E z;IAfpHeraB<}$Kw*70{b3z~X}*+HjSMeWUWjFrxg9h|I!1d9zi3S;o=Fb(5%@rSRJ ze>NNK@H0%Ed{$emkeV$n&Oo-n3utS&jADr})#&1^txBww!1$8$m+3-HeO1`8z`nP~| zkIO|ChM@qyG)P`BaH7;qya+Xg2-xFN=oO+kdriqg)|*!w%hI#Rs%#-8`g&taJN8hH z2U%QdtutCP*t&ebdj+P*;V-n>tDU->!7M&4#!3TyUr*atp0%zyTF6Uw1Zb0yLou-0 zYNLg|IO&zc*h9d8Iqgx%{xm8NF6^ZP%85vODe5$O6D&-;I|Z$i z56!o>K__9AGS(2S2wZ@T(^{5CoUoTXqr1X5af|V(#9ZbyYI5}K?nNV%$pUj+M{~IX zF^OZQ2sRl|R)0^EidIKev}e&YJv{bL06ADl@N3xKMOj3z!$Cn0l!6Yv-AHEDXFe$r zk8n`*A;$Sr>0Zl6GMCOrf@t_JDKQm9!w;7>wf^M_l2|N?^D99T`}U$F_7vQEL0`xu zF}QFNfu_3jwO{!vSlJIkK%6%qo&;_KMwM&w3W$iPJYf1tR+MyrB(lN|r~&at%3TBv zo24vUC_zT8xf&LUD2uy?eBgHbJ;JT~@%Jo>feVedYTh49&>|o`>zFea zg=0`}RsrzH$fG&K;E%bNLdSF9<2bw|yiyE$Tpc}=)NTOX=0i@kQ&~sJHb>$=&{Any zHzQ&a`w@g!;h%+k5+tW#$$c;Q|5*V4->D4#|E>b~$JB{`AHe^+is1iThVC(Hi)4d- zLhSqzddKy|T}=q~FEnX*q_x#)Gs_Ru;v@@tsRp3}C~6x>HAW>ju-_t`cyV#Id%=c9 zh{$SVovjzqVv7K|zKa4mrYu2g8d&NB%IWIP7C?Lpa`zT$Q46>$Cx+BD4>F4wbQ*U_ zP}MYyYV}ZFMb;v2_-f17Gy+|Y^*`LL2--@?);-F_fYl*at8^HPa7VNr8Fgz45qW8k z(TmH-S7}gxk%A^L)}n>MwiPtV%X)y!>(XR~cm!6Jk~`wwrRqd6S5HJu{3@?fttE7y zVzmn42n>k`L~#r@04utaH>{y;z?LvrVf2VU4@C(_0c{Qe79oQ$#tNo93^$9yo7fGy zpLW(Xe4Wu3+*|-0odU~-zKpVKi$ev~>QvObGNAwi@ug8MRnm4q@NN{nQHF#;%S=f=7vo^|O^z+py z)}a@T?1O=jr&oNjsTP$h^7b(D+cc8(i-a&&F&y9=$@>-u_=~{#K2(|WeYgVW`%p+4 z{7B$@-(BQE>e?%oyTE1_0XizhdN|rLkBo?WeAt_Z3dn=QxwHDyHdfPU@HD! z2mJf9%J}!6E8yQp1OEMaz`yS;;@?jJ|K{KzcmypSN>EVvZfvQ3aHT64;q81Jj&V-G z0w0cRTFxzF6i`&inUeH|gpJUME6B4YJF3^tvaypz(F&IJH+0K;Pz8DUbo=PsVlghd z4X{=!Ok9VMT;hz`eXF6LE{~=7(jKKZ0sXK__`gw3yM=1uD+Y%J^G7dI+VXnzKK*_u zyo7Vxk%{sa|1fO4(rehWd2b*V9x27bB*BfMiQJ)wc>HZZ#=ohIj329jj2{cg`0;>@ z-(N(=k0FCzZJW3@pyN2&?7`o$3ro_vxGKBXTAQ(bnn+@}zJh5=iCMQMQNLs@+1pU2 z1QVML+=hOYLappZ3Vmg0ae`1)IYEq5Czj=s*)6>*RC zGo8-EJDXG6aBiZAR?}y66)=6a-+Iz#d@bpB6SbjMbiUo3@0`VD%q8nAuB+EL99(+2 z?+}s=gr0OcAYH!7I7&(#qm3p|n{+8rM-VNWWq;y%s?*3wT=s)f&&1D^{Ys4z-X?od zZ!)#As~1a(7XoCOD+{nkKyft(RhTb8RQNrTKHuU{myvFKb{`(5`?j|?3*IF_@SB@U zwW$lUxE4)L6c-clYp@%sAD&LsPBobzDV3PUP8?>3(V30S_0BHZe2kqtoOziBhp4&# zDT+dU@FNOD1Hm;ucBZk^Tu=U_&m1+3R|?;w!}@Y#rHLSDiT@K?q;HvqI4H=Q{pHZT z2f>5T^4ieyS{s`kWQoMxOix{$o;o$NaB}9psRw7z&n?W&oIU^G%)*)3bNBl+0!#{l zU`&1XABRlfT76FOGG-CMEu&FuOdTU*RMEd!icj~$60JWOxfBeg$X-z>*krBHdc&TyS1NOe)5SSc6 z5BlnXp+$f}Z^^UZ4u;G6FbpCB*d>qR=mL`|j*RlH@hVWKia>@lN}z1A4D{kwd1UGi zncN$Q%FegTu#2Mhp&WDpn7x3;tmME8=}n9&7&3Q1lGs$82p&pjiqfkkxGU@Weo9D( zYcc$O9RL}o9jJ2`un1_48}(owt`EUd^IZ%#VpYggT|of1%n2ItI)M{d0U+jG;?5H zCV>`(HSiwOxKc>#C-C%Lyvr5&AVV?SvpAT)@8XZjHYXNRe^m^{(O>T<{Q5~G=kx`y zi73|G0~rU_eL4jWB;E`Lw@f5Rff|H-@SBlw^zcaMU<>BdNElPu#rI`B;QLTw_m8;(s3zLV;F;?}??UR;?^ytD$JczNIxFAIF) z2a9~-Pt#bK^entaYw*vp1zs85h7zWE7Sb5gY_r)&K13P8mbYD55^EW(M_Np}FC|Zd z>V{T%^6-~aY-2^60Mim8Ai(?8Wxo{~5Mh0T5{00+&TlGJ%g7hasH=RiktV+2o zBApAij6#?V4z>`WP!<`J3u{5tA3br$ov*p;OJ4iBaegTLAHRP+N`$+AsCx3zi_-Iu zTf*&_Wb^z0v9G8Mv9GKEv9||^9SsosLq&-F95L-OjUC3_bdk|i&@YTDp^gf&`bFkt zNk?>m&E6{ltC>jWQt)wz?}?x}PzlutpD1}B|4>T-wE)1NhP&KE6e<8Jj;lw{Kp;&H zt70IHnq$<~l*A*t2E!D6udAb{5tE?Nkn5QlZU5B%1=&qTfPO&$=&{NGeWU_F*8@Pm zIso*0iU9qpTNCrBh3u*(1Xn1!7iB;WQX#Yi6a-l$o%SQmc8fMD0{^uRZJ>09ZL#xl z=4$J!^(~Hw9~>l34H`ApRi+;TmOLel*;)oWFJ0%R9&I*ia~EegCO_Fy8C-Y`X?#M@ z5`(L5eQfz!3nw5i1RqPuEE!Uoa$)MI3?!Do+^g!_PPZqEkYG+p?ohkFgvP+Sk~mmP zhAvxyFBG%nDo?V)Ui3I7GaxCIhC26x+>hz{%%2Cz}V z9^abLhhH%~a}NHvErhoSWTnTiKT`Ox5qxECOY*7HGk4i6n%O->VxY0UsGRH7S2fx@ zs-=d&iP{EddT-K_&dkRb&mF?2!)43Yn77sJ}YB2@FJaOeyx2 zsIKYDLfZ8)3uDYI;Xa}h%gGQG9Zj?;A)6evQIhGLAWX8oWqd8i#tN3&QjOO1iJ;hC zK4iN~WR)S~fG@omTAgn^9Hli_={Y<5-ke6er-q?NDm8 za$xx}%Cz`#pxrxAw0?6HCMwQp(6)EFtO2&ZKC->lXln;ljgSSeyr6xZ z!7903d|~kx_?y*IMMu}A37<#4T(nl0>~&FMo=XJLEPDrbK4ydpPd0XB@0T%KoX-H! z67pkyy~^%9eUsuPtY@4~JZM(#EU>{ic-hn;oQT05N#Ky#ab=&hT|ghuA|`rB7&Kdj zhO*c)v_D27=!QvRRFI2wSpsXRc!{YpjxX~rF8a1`bcmRZB!`^a z-;YJlo&uN0!s>1xf?bU+Qrd1jDQMb*D&;{u9No;_;oPa{*nq?jW8)l%x|{tO@H#|_ ziM=}A(|%d&?SZvksmxjzDzMhYz*-xDwf<<4wf-!OgF+9Pz7T*x>qR2jLDF?H7Hib( zX0V7DKIs%XTTnDtdQo0@d3M=a8$KK4st9cq(8yksR179=a4(U%2z@>PX}Q@E($6$$ zF(61pD;yjg)q-Y}7EpymWsZcZ^yXsFlo^hnG^ML4f=){mcgY9v8)PGav|dZ5;Bq3* zKW=nDS1=q}_vZMG1&q5|8RJ?NFm5ei+`|FmeyoUb{~Q=+77zphgtL6my}Q28;SRc;JW?%UB$Z+L6Lr`HXHm> zf9O>-L^vn2T9!mc7@TP;&J+;_7v3OuSGcKayu6YdOxgD^8A&fGa#2D@^c;)_-0N1x zy`2iU_ej9K>jC$kD&pS95#$rOoktbd#+17sRxY8LmLf()&2m__7c+%5W>>jNuVXZVZ24sP5gF!TatUFwVtxVO%_OEQ?qeE?^sN+3~H25 zDl8H=7gH2%RmFs}nC_{+Kv9WlugEMu4RVikS6adpo~Vxc>CZD;6^Y1)E=mO}rqn_y z-`?43vsDPQ@Z@{wWKxvH>5i`ZGM-n_j5=bx-Mh1`f zNF43;5+48mv-d4vaurqnf#8Dyq%K&BqlTHFeU?9L`GI>B|CQK3tCb6eyx+l{& zGu@Nvo+Kk6AfiEr1vIz_D)?8#7XrS3Me&civiJZ61!Tn+A_|I&!Xkpp|94JRox1n- zz1`J4!QF4aKObM`&V5w<&Z$$UPMxZA3QyL|2t-H1Y3H?^i@SUZ(u|OV%$RxpOc?$Q zNQ18$qN!`GnD&G~b7`cW1-+fEs7_ETd^3cxSz=i<%taiEO8%FiAd48PBxO@Zs(J$4 zutvNqdQ|;NyeFu{x5IhR+KYIsDE~{GmH+!3l>b&y{+EjKza^^tOF`w2^>Z6Z!;=H8 z_9?U(-5{J;VLR%*;6?<~P+j?fv0|w>SWYdRz??HbP)c?5c7^sV(5o|I_t}!dWO)lI z&E%~$+DBJafBw%Led4uxs3pa6eytUIecPy#wQmr;(g(OxIjv%~6?!)ilaKqPXz9Zk zEsNW0W++!K#p5d_iLa%>#O1;&R_iX9we*Yy3^HXlA;#-flq#Jn5U*<#RBzz% zb`pKYCBwoBEG)wUN($QtJSWa_p)@SW7+~2)(Tlw~708s!c5;|g@E!~Wee=IHmj47% zv{yST+JAOXwAYBD{jez7+oFp0OG%1Wr5Th+)B#zy=_rm+W*9Q~WJh4D?83)QmE4-P z!NzYGls!4?5#g8W7k_4rH|{+)M+lqEkDz|5u+lz$|8EL3!>fY$nCvnu~5KrwLNFMN+k@ zGzv=#E6T-z4V3L9nAL>M7kEr4OH@ooRxz2Dk@8cbz(Cby1ypKoSe*QdI`4pAME4t1 znKohxlmvm+`>#uAhj8eLFW(7o--qZ}#{w<-o9|ocG$Q=n3(bpU=b#xIRhsQV3~Co) zm6DY(CKVnmTZlEj`k+UIkDW*jJi5%Gv`N)N60WBV5RpR?ehFi0=0)4i-`3*a zi_30*JdKw=<6xTIfgG@ECkNWRp?v&r@>xi`atNwKyDGv;7Q;o6jPw?gUsSLjQ0j?&GSwJsX+AJp$`MFIuV~ap~ z9)KB=|5OoIyoZXfxU%^<+#}fniF-pb@XynDE#nga{~7v1$%QTOq?NcyH2U^XjA!XZ zE^rqr@Ldo_V%FbKNK3c>B^t0^#7}p5+eO>dU4JEN>+TNR>Gp4@_x?&h&!as1Znu9I z-F%6DUer%=1~mhHnd+w_wYxbm7*tHhAD5c^$G-)ROZ`J|YwNf$<^cSu=wN)zm{$!Q zlc%}1UkWbse3nf-UYNojgi;C7x!ah9I4&c2BnAQ5>Yt371%_>(7pSEe#*U-7Vs=ia zt8n|g9=CM+yn&uRh%(RE$A5_a>2YeUCMkXwx{7P9-S8mPHeVs;?-9MrEw(omvJZZO zEp{ipzaKr>Kk|gz@*sNhmdKNpO}Kd|J@Fz>?xxZXrzbt&9n}+Re{h+((4x4|#-iZQ zp!X?GJXRdW{iD$3q#3Loz1K*+r%S!NC)C>|^}bqPy*Ym!s=0Jrt0KRH)KVw?ET*5O^m7LNbkR=_ z{hUcZXW>WmXvtVW{;AS{XBy0tjW=LA&3hx#>1~F|+FW5?uc8{X0n0@Awr2`7JJK!| zXsZo0{~WpskF;xXOOLec=qZme)6^Jas%!Dr#FM{V>gY3dOg63`K|YLQ9MhD*fGpLK zzyQCGS}8KVSlL=FK-U|f{+hmpTtisPq49r#kJ-?o(3dO?0K^3Pw-bxmQ>< zR@V@h+BuljLN^JOH%pC&Cs29ocB1kYsq@QH=aC6@-oBmcyj|+NOX{4FQ0Lv-sm^am zo!^u?XC~D7uiL54d!^3%q|T$`>%{D>y1+OMDfdf-4@iZx;w$tY+)hMVv+QB1^AWm= z=_YKJO}U#qE>u1)HO@|;@|W9*$|t4H-$=*!T+lKW--?pO-qH zlRD=m)cO2&s`C}8^RH6p@dvCFbVb}Qw#&N~nkgw`>GPW;Uy0Cra(w^@0iZhp` zJG+*zJSXJX@V8O<%A?0~Zy z0OGWG5dS40+8YG1<0T-2yTWw3Vp&;S3F3jbiy+ul5G+kVu%s%2 zr41t3M-aSO5S)>KU|CfJT@4~QSP&c{2)YswEU${-%mxv>RS?V&1U(4|R#ru@szC&^ z1;HFaaApF6)m0Ik(;$Kq1i^`dU_}Ljp^g69*y{aL=qpwU>l#F`KoE2Yf>jj=eC~~X zu^>!u%ruDL3_-9=5S*QWAX^ndu0aGV1;Hvoa83e(p{fY-4I)@42;LzG)+8Vpu8Lr! zK?K$VG$;txRU-H%JVm4%Z#Vjff0YudLvc^bOuxv3{ z{B#aqXfJ}^MZsOJT$R=1wOQ>~$1);VE)*=81T0&sVwspSmQ8|Xt6&*Oz;a$yEay)d z%LRhvO2Lv#z;a<#EEi1~%T~d1jbK@yfaN_^v0O4`ELRDZ8w5)}0n65^ST3D1mTLve zrv=M~1T2$Pv0Of7ET0f8w+fb#1T0rp#d6h@v3y>z+#y(s7K=>A{)2S+HUq}Yf&NT) zuQqq1+iuv__|TLg-YyWoDG)ctKwM0F&pvFx7>Iu*h}W9CA&CBcR34+eZptXXB`AL& zC`&OY|4w*5X7Cu4%*gA_-4IG+GTks`i1!P`M+D-yg=nx$r;0vdu*6PmKUp7^s554m zhP(p(jML9~^m8%&yq|uqqMvK%=X(126#d*nKX=g2J@^UvQf?$HiQbD|nZ@!I`%;c| zAIm$2kX18>qgCU)U;bHCc+}Bh?T9CA*du!P=_RbL%vD&pC5`Sgb`@f)~YxF~wUk36k z0di7f97thraV8GO6An1`CvjyQPYRCq266BPq(T{4d_rjar>JDsP``^J3dzi)^fXas zo}s6Vl&>0-nN_1;7WPF}&JkT?<~f4E71u2@s^Vms`Ad{DvMEg@*FRz>!+{qBNL#&a zrGezLar;&M7ad?cO}dO5;w6Dm$B3r-kcKh9Lu)LQmmQ#3>HWJvsatvt6xu!)BfYOu z$t=CoF2+@`Ux%dk5AD53Iq<41*7z1aShS*bJxUUzdmGo*DNY|C_A0OV3&T=C9u+}@BmY-N|_I7|}CHc() zt8Piwu#)ufepE6e+CPdYB*{C`(?m%=h@P?}^A!tJ9mcd`;No~=+C>Q;M)h;8b-3fC z)?^tzJj#)y z_h>4T1*O#i$`t}7H8m(m^@TX!FqAn0Wv&C1s|3oi0%dW%BP4M}AMu{f6p&R*Mdycc z%@bU2i{c9PrMJ-2#J+SQJ>^#9E0*fx>cvZuw~dYorxF0Jwr=5}YLk0Ydz33Ny~)aO zhrp=Y24)y>LflDZvJfwFz;ca9&XOr&Q8D~sX*pf6oZ*1wTEWsKSn4)7nJ;{LL;pk= zOpkzB?f~W^0;X5MoY07frpm=|Y>mHCfUI%=V%5yq0;Dl%iPJOZ2$r`yV7XqHvR1Iv zZ31%(jnW~`B?Kvl+vptvC+z^|;{qoma2o5+?9>TEp&d4=9}PG_u^KohP?j_#E;Uu~ zda8gs_)rvBNCmgi(?k`#fu6Do<}22Pb-N-~@yQU4D&dU;fh(@Nm#d1C)o>}wnHV+9 zdzhF5<5LEbFXu*LXLFmQKtfG4OivS==$-VG0rC}dYM#4Ht)yh^Q*#zwD2%B?;{@tW zZlHHZ`4ZDWf0pk39>GzE)fvR0Q)PsDxVAdq>wx6_V*hRxBz5S$AQE#}RtSp!ekzvx z^koikUJy8!3mo?dx^xhZK92=nZAuOs26csiy3zsESEaQ+AfVi%8bhFhhXRBlZ4*dW zMEuRp!@D<}brcQlRYl551lg|iG>M*ldlxgCU$dJ!fWymM1GUN-w5WZrD z)G_5}aAfUNm68}=y>G4ZFAG=dFl?maRGd-%RVtEo&Fyp*bnI2Oj&CO`cTNpT++5`@ zf%3H|lu&oRg`OsM=Wozc?#_J0%~Xevgt>)$N*yXccD;>d`t-jN3a;BdeSy+?w(evx zzBkIBn5MGh<$Z#s4qsY?52-3+=J%p#Lap_G>1kqX-A_-swfKrTR)_t^C{>!ODK^-T zg-P`eQ=#gU+w9>em#VheqXMVip*#$yO0zu{1r%zw2k2>Hvpr5vx!L%No2@YuNb-f& z+td74#fSZbFv~qFN6^F&3@LRuVcbyqWRzPm&3Ef`!dZ{6Z$ z8}mhi!yuM5)R-1T=os<1)_f_-wHPV0t@*M5tHZevXw3ky_~!gORlv>pUr}J8=KM1~ zO>E8`Xcq)GCtooq>m7-drP~`w5?i%FW7zgVnI^q~@Gy!q>uk?T6rmPPSjpI#>I)iH zAuD`GdYZ_)U4?gi#i;95w6KV$!nkXMaeE2lPOp=35eTWkSyXY|V4tcS+pQ|c_7jfr z6?4ozt5tAVSXL}<7$04f&kbWaroO!r{=PCe9V|@it&?d=g#}PR*i@=?Wb0HXy2ml^ z&?xg_I>Y_~@38F%ui|LmICzIUz&lXj9VzhY?W@V$Gbaqkp_}1=&iXcH3Oe`L@B|4< zCA&F}rdn7Fw>qFZSa_Ebbd8N$LCFi8uN-2VKS#jLbpUs$fIC*eE!_^?V&iyzEEicc zwJ!U44#*A@WXB7#o+%>>pDi8c+=)~VH*1>%K0E54Ecm9*x%grK6hU^X1G4{|4mx;v zZx>|UQ|4UgWZf|1It1RrD7=tWvOhgd99b9BQ-;k~JWkZ%-#T+VH#V_i<8alHAx87w zB#zN80>IVQvC?9yO_iwXz@}sE@#~3lBxGwWsmhVp(cUfQ2wyQY_jP!V_3F))#)o4) zT>j2fdh!nOU8VBf=!z?MH*^A(V~{M377L|Zcm>Aaor>k|wg+7W?J;SlrJM1D6A$bq zaP~HE{0r=6Vq*?)T;0C(2Jf`Kn<5&V`X?b{93c9hh1wA6ML+WV=d<^4 z@wtMks?mVf{JctlxtpI!z!J?*J+OSx0hu*DuNGueXIY}@`Oj1dYxHXzz+Fr4uwY#$ z;M@(oB(~8JK4f~PhjW{9;St&YsNnmU1HKys-^T^tS#WFkx(6l7ETe#*e~-+4*S9U`tdQ|85{@nJ_n%R6QJMU?m&%fQnSZ%)W4r9ci^R)IXzd6E*eE=_z*@zGBtrzV>8S zD4bRGuc<;8RsAHr$aq#bcZ}+IYU%2ysKy$)`bJTMzZZP&9k&KPrTgZl^|3!`oQ@m2 zZ|E5Zpf?H7KfYE#kEsdtPYytzqjy*W|13b)ZLb!`DN4Dq63%hc<4Pq<{pYDxRxvL) zptUOI#qE#QLC3r#X#bXoHcrR9V$k}RCFqz}=?&H~N7Sujc8r-bUOpY=v9o4EP6XmB z?$ho&0Xo`ObhIz*OfTqM(tg&`p5AnCXIIZzo#|yuyU$Rgntv`;z@h!;(G@jjx7tJZ zPWn5d{4B7@Az}<{jZs6d#F*tq7!xK%L^W~x9c-cce#W6ZO(97Wt zzfu^$$>}(xj?SR-TKf7l?R?TLN-K^v_4TzA)bN9ut={ILJdSbOTpShwmDxC z_5;x247AP5uP+qGayV+Usc8Xmp^fWm>wA}T&k9w|hYZ&&Ba<%%l8>aA*=c4`s*;+f z68K&k#9`k<`I3i;H%ppV67d?I@P2*aHV;jUFr4B1Kpw0tfZ}{PQ-H`7a=C0So5GQM z+;ciq&*`mV){Ned{y~G>dBpH zf9sm|!UXz!4Lj#v(%uaC&0)aGtqA8mBX(Z`NGPQeaPsFtS(ZhLfiZfn|{xUm0uwj5I7MzSVoC}lFVG%kI0Npq~R`BL1hR_Ft^g=!4{mC2+rtZ zRQa2ZmYFj>?{IIqI_$H8U@$6|^Okom_vRgUoJTFEk7(l~ij_t(T+C7=HgbX}<~5zB zrDP1rjc_7Zni)tz23}^MOs5f-cz~HTk}p6{dHOYx49#f%s;O8&yU>xa_Q+O0U~ zsdMFJPL89FS*g=NqJ|%Cw-RnRViWvnS$4!^VTbQZKdxfSQ?V#4wB70UbAwY zoAj-;Eo)>?_r}la-uSaO7?tcF3j;;g5$$TrV(EzoMtVGsfg3d@i0Ydi&f{0Lq5}HE z=j;nKgmishBgVf`{ox-^wpGL&vlJ{73YsgcH3AZ$q>fFmk#VM!t}HT05pU{XihiIe z!oN-w*-6WpLeZfSiqiQ)zKpXP`ng{it?plLXEZt+7az;YiATEw$A9`~Q^|XrgV_P! z4JDF7RtLuYfR!DZ49Q8X^YBNe@;p|3s7s)Yw3}ScpF5SBZvp*u24&FFn@*-z(YZbc z$8Byz=)JZGn17SF3))^uqZ*gpT0L-M}-sdYC+5JxuPeM}&o|gF&^e*$@ z?W*Q$jY<##ZA;i}!ROIuT<}$u%>E$V+J+yNqF9FKRAqx|LVY!Tkhr{J&yljd`aMG8 zB?if)`vOYhWG|H-2yeU4X5yfMYrAUf@m8CE0797aTMaD#LzY2-TzJy3E!R&1Y;0FcZb+G_HYM>Z^oD=V3(YsGCL4ntsyJ6TL z5!e?g*#D$p-#`^QG`EHQ%^MBu8wB>psT7qvIl$LX7$_mwpEP&F0B;h2S1N$lDu6dq z(a8Xf29q9QyWX?J4i*5u;vRA*l@`UHTr(P@MXY2@ox^akd^>l0r%!FsiMEEn&KA6rm)>2 zFIDV8G*1B%wWtJ5k-tbmq;#$OsFlkUDj>j6Y{HSS$)8uts$#+oO;5b^1osE zV+04$p!XQ=7=6m))HluDkbL-0Q(@dlKT<$`t=RT0N4D7pwByvX0`S|h0Ka3P7=WB$ z@!sfN9;g1z+zrEiQDFZ@!Tzg)eIHerEZNf97PcLyUKZHjrBW)!sqY&oA=p0%-(@qs zJ7}VJ`<(jjekzk@N9Ao7Z3`3gdh(=k8~lxl>DqG#Ma={BOre@tcWg(@nk+1F6hwf7E+oLQQjpU_8jk@HpKvhxoVavr8q zD#&>>47}Ut>?%Jscf$-nOd!tI5HlL$V|7D3lFH(-^{oQ&-(w*@Zor8AP?*!t!*>%o z{tGHIg(`^SrUcH-^h7!(JR~d`1r81fAe8p@(b`bYs#^R4k{lC@qKeD5f>3Rcw$VER#Kvjzxs(CQ}3+^YH zu3fL_>i(|DeZD%mqM+l3<4+qbbsX2u!*OK6D|s~T0?db~Q{Z3c@%VEFmj8lfQD8iN z(J;ucjq@b@HGDU&D{uhLOH}5UP(`rca{`{f(UY%6p72)Pm+8r!`bk(2zfNGe-@mF! zJz_^Ch_BE`$$}^?6E}3;L#48m{GCb(NeL{HS8=maMEvgvmbWS&O4n?SWg3ZbU0CeE z_g%rV1C~W3l@1A;I!Gi$#k@T#K{vdjq}t2)+!qI4i$5 z(v!#ZlW@EKT=1W)@t>*j?_4+jUsGAESe~FOZr7#?{M~5SU2!*{&%1pV?cL1XFvO!YYpGt@- zsl>hpk3q?~0QL*tjguJG5&KiI(=nf{w8q{*vrO*qqne_NG(`u{M-_eS>K&%-Fpd5* z>#5e2XFL8bGKt+)Cb0)nap3{qXCmI>NW{TZz+SOL>?=fEtckcv6LCl^5!J9CDA*4Y z?1x(HhW{M9f0%(DnrHf+!1Z8MV<3yQA0&p(@mPwu%g(C zJBB{0=u9GqRjjx>Xx7QfmakaZ&Zp91thl?(thehIy8a!eJ6Ume%hYxr6&)HAeYWC` z$NkD-(X1dnt*d)RlY2sSa>Z7Nv*NyEu+(eCMYfYwvEl%etvLK!!-})9vG*BR{)v?= z;@3`!Ws!fsG|C@sqp(n%T%Ai*gd#^}vQY393&kobEk-DYr2d_!E3!Z5CKMZl;!`Ur zW}#@0rPv6Cr>i?`y6OW9s*_t)D9Q%Qq^omKxM(ezcmEIy4$uGXnu!f zlTn1QfjSM7jOJkn_abvQtU)f2#yVU>S*W?U*pYj76t)hxiv{A6Scs<^Fb3k&wB~U} z^zPd!A;Gff-L-VLi^?@BHcZk#2}z4INn15Z-SkniQWSx(Bw1(N2ZW>^DkW@Kfa_;Q zVX_agH+q*{Z!4&Tc-Px?0{;CPJUS0|-j(!GGWaT;H!X%AUvcN{r_y3NZ@(CvU()qY z+sR3z=ftKwn~Dx=^a*mCorC+y8vRGQx<@s+Z?8_Sw3XH9AG#Tpu#OrrSSHm(w)4( zvW2yYo-&&@3oQ368ev%WfRXr9;3fp)k2Oa8%NXBT6{B5}J5Mk+*p$hua$P^snDB3n zru?qBeJ>C^4K`(&_K?MG5a@bX!@<8b;Cx%)yj$S7=gkc@A>@558n4K+vqv-<{96Of z-Gb&4LF1nBGlYf?FdL+;)^R-#nt{hk{2PnNYVF_23i?)q$X^rCN?PyHGOB+Wy}=Ye zrptduYoK+G8pziBQ^}`th1B~FTW=LV%Q1AWULU<0Kc;<1Q2tS)gj(idAyXY?m0{s} z8fsV(@D&dWLsVMKu<(CmSemZ(Id#3oZCJRGKIEdamJw#u=V77a>}tcp-nzPdG`S1$ zc9l`E>acLL!7}N-N14UbMside832*x6aUtb&s&A_w;4$OV1PYA`TR1y!ScDU_sv}=8!n8l5r(Q>|stw3w=WSuwP z>n;{@t!BS>;PJxzK$+fY^(aWgyt$3FsoJ*bw-94?0?*(t`8{giI^OU9ES7~wIk50c z)K9tLUlkS}P2cJ5nZ!cSXIZe-kFtQ`?o=Lu?izqP8~}Yq03AIF7~G}{Un@YJ4uEbK zKq&#VsD42H9Qs=6-s;V^*a6g?0_qq6wWI-1$GL)9>Hz940rfTkb$b1v++^tt2S6*E zjBuYQfR@z{$Un)IOI;42?iMc17f{^|fa0C@F7njl0BY6-!>Q8*)bjd4MVl^OwV&w# z$fmLB5J0;{Rr|XT{|0NI%;77!u`z_uAX+lxWuZk$-HuHlI8=qTs6b1^3AL$!oi>kD zt$vASiW!6!Ai4#Sm!2vKArr71%1FnP!bdbxv`Hi*RnQk287kFcU}S&-Wuxd7YI^dL z_$!G85hWhatg{@Lbqy8CO8j(T*6vYe?cJ+Fn}{Q7%nOfgP87T<6r2-7!6UK5&>9B{ zP8In(Qz%$V-vx%D$vtvg268-*dIK3mlGSJc3O?}PL51k1Flsj*X4V1ba)H?=Fmq9u zdj()ZuyR|<_Ldduf z^5!TZ`{+(ehU)zJn=-@WxrX6ya)!Uf0sg4KKTqKA7lq$v+M(F*BM_Zz>Ji9QZVZao zP^P5}t%TT0ex5Fk<_0_jV&_Ii%M&TDH$!pdE1_{aS*J^^dBdzY*O?XPJFsGlu;OB2 z#Q{-P1Y7cyDYoQ=&hX#u0RKXP|9*jgP!#@KbaNr}Y`ZCjOPtAgp92|Ng^a6&jDw?O z?7aljJfuCK=-Sczz=q+*yVLuf(N8*{zf#a&Bj^vUKyUgCw81G;QwCQ!qrb`l{WXIA zdO?3!68a%Ha~d@`^pLX68U1w*=x-AAHw*g1qv(VE_v9(|-;X)Nzrg|iEdu|`0{_S; zd}Hm=KyvcbMCTLE=x=mDf4iW+OVH1Vq7O3vuqiVC)6Vce>j3{70{@!=e`XZ^5_Pr# zXMGt{R(A$DLpFPASd4n0+5lzHXQW?t zKzhF*eL#@TiXffbjzhxj&gk!UK>xU)e_YVdj-r37wb@G*LvyRgJ-u<${T^q6zU4sB zlS0sMgrK<*f*KW^?>Hm8DW_~RqsS04ut ze0x6}<&^KlBuN=S#*rfYL1XzWWy!*X0^Xp3@lv@sLM@jsnZyw-lrUx`y+HpLHWfD@ zNlGp=hD0buYDq8CKagr)&_OAOQ%E7C^G$HRlysWDURw4_NU()OSX#fY92yrqL)zN}v^HH9B@ZWQF2!dz(>4yg4-q?sBTA#9W`2OsyCWLuE5 za+4vE^Mh4>){-e?*&&AfUjxMvq8siKV9z$L7k(1c!tq{|M;uysr(rk<)S82|@T2ry zz}ZtnFR*XtG5W^IxAQXxRJ#bO*I}(oEj&El0IFZapo(|o{K^5^DL3LuJH%y+; zX&hrIWROf3isc-#(v@?_Ry3;#cij8=<| zY5Odl=7#7GnuokF*=Tb-OXP%`Bgbo>tFMKc6o}p!`Bkz!7gk%yW|c@&!e+>0Zwq|k z-e4v_JkIHyn%X$6AVxU7q`72#BvY6*mdj*0&s5+e}^W;NPMa$R0qP_EYP1V$(GilkC)!>={=O44FR)cl&p0O=u zQ{}M~GmFwmI;ULo=7&kqQuXt_sIr^@$rzN4f zc@{Q>{h zo5Lwvn~*vUOFxvdqfpis9&*muEa<@JY3=~XkJ^Io#_o_Mn`jn5;i4_!HS^dn?qy8LUFt_ zJki?JpM&>`(|&p*s3MP!S@sABjqH?LZUme5I2ol~rN6Xdl|C>9o1Du#HrqVO2HA?# zq3C6W@m#uCNOOm7LlwngY*k>JM(sfGkE5Mnynyw0Vt-iYBsA2k4h{9B$iQ!ll_k7%Oj3(>nc6TO=Q(SH@9UlO7_qeQn^q6daD zh4siVh~8y(WYIW?oQ;&RmRS%WeM%&^3(0#qle~`u$va*GSzyC;I$g0oSsWp`4l!*j zjv}pOadU2LIac3jEhHt|)>5QFbZiFFp_Fo|wAu-#TuZubJB82#oC!V1fzVxq&|QVl zrT<_;SB-)N*lWux=Szi9&zaC>2SWD|LfXE+l&(}B=~h0sHU z(5}V_J-!jq#UaC*$}@$^lrxoc9H@M&P&q@W>}j0J6B?nC231YwDj{>8GnvObkU3k( zoFinO**KXE=^=H{oF?=fA@pQtLQioZ^aLUFL?LuVgwV-_bhef^KI&PEiVduzIGO>w zFlpX{5m%n8Rb?zcV^WMa#|w2CPfc7b@A1^suM_G!oT=}0pnic+-yzhmicnw2aC2gP z(#%_)L%Jy=be`@^=Q0O6&k#D737uy*PUrjv=xonsJBq{OBLxJ%@PLyOTF-Q*b%g`1 zD}~loLhCt=(|TG1wD!Vjy(t$aH!tL_b|&}j4&<&Aa^E53u4$ayx79~3Z_np5fVoXa zgvhirkr@Xf?Kbp5A#zT;t#mu;A6)OwMD}0(6nkvDKN5_dC$BRp_`z=vd!49S26~uxFKNLM{

oGGBs6(`iyJ6;eLrOv*J5q+Bbcd|F7^&^Rdp*+Qp| zq2qF)&nBS zBN^+To;{M^lFQQ3RAqJCOGodrMcy#ZoAKZ~$7HOYtrC+?Vxfef8tbb5q+tI}C3dx>IeDK*AqgC$5&mTCtq85#(4U9{DtK%gdwqRJaF@dA zX0m{y1hh$aON3(POQY27a@Y`}7j_`LCHzT?KL#wt520t!7`(E$hQ6kiMSm?_nTVbE zS<3?sz}_J=rv=y#8vvFOVEsX0@#g=J9Drp7SWbXVMEcX@Lii;h^#bsvA>7nj0)vhOBdqqAQlnn7$>Z_L($f=km6a_7J3Axl`2Zk1|;jTdROVE zj}oO$%YctL@MKhYvQc<)Zj>jd#qngE7Yn==#iDdR>y2b^8YGQWGma?Cuw0RT=MvC; zY>*#Pv<~mq>sbjb+DlmUgfoktbYRiDghdw!i_Ujq5kkD^+{f+Aq63pzq=jWa;lv** zIidHdoUr4xX+2|E2yWI}KURb<7dkyG^_#IN4vVB_=};eGmfMmW7{~n#?eU|v^$Dox z4(SchyjW4i?cx9*o;EmE9Kmp4@&WbrVF};V1NP8?oOsNJ9fpu~eSJ;kp$v|G#DQw+ zV9(6JSP{o&(pCXk&!jDcJW7-hHm8g??AfDJ-^l4q!v{TRQbU!Bi=uT;Y*d^eEHb=f zcv4-P&kxN=uqoSydPTUe3EKo{hmL27RGWSkspEw_>`XSfG$`6V10L`Ih;G1EC?fod z|@$-e+hcUKpq{7=>O}`o`@@f@IQ67}`fL55 z{HN$^wl;nj0H$=3wpOaj)pJy*lU%*zK-DvZfmzL0ELVTCR3({b|5dwaBUjG}w_dg7 z)+twi32-XGV4U^{2&u7Uh7%Anu`dXy9q79xZ*siBsD=H-+MsrH0QK||(*iFEsOb%Z zdbu{Jog6?}VgI{;dR_gX{8#B~7WQcu<4UUrcVT~pM8-+jU++NGbi%;x&sQw$Z?IG) z3HvU!i#Ec(Gu6Q5?rO`eQ`mP2a4JFAcdL!ax5B=gfO=EIpmwhfYIg@voa~cZU{3+H zNByAG8Kx{(d)EfGrvor6@NX7ido=)TKl+-1?H>T;no4R+&TsR@BpV01pdIY>Y#=}&8Q9PEe@dgPzVM! zQ$QVDKPdlb`kLjcwKlLr9DwoRIt(l&zz%Hy*c<^iw>B`(0T`dt!@!OeV23pTY@Psn zTL74QPu!R4bn1!C4pf~;7?>));+}Z8r7Ec>&Zu3q>4~Qbw_0qub@s&e0H+dq;>_BJ zd@J-F0_v#xLHV8ZH4FWs+Q5!>0LF*6a4RekV66=RJ6(XCQ5)DS2Vi_W76a=NV5tUx z^$4)#wSmoc0LG_tF|b|%Hm3n#D+So9+Q8;I0JHk*Yyoym1HjG^U~jJt>{th2d^Q<# zY^?x0t^r{05Mb%rz~(ssSj1lS1;09#LAv$_}x z0CU&)M^c?m8vjHGsx}Y?W;b84#&5G!C29OqY8P!Z{zl=}skYoYHGV0;sRWIGT5Uw? zC`TrCTtKxq3~F-#lza1?O!YZ6-vS4U-bomkBEI70>#!6hHQ(aeMVsckP&l>3mRo1@ zy*t3EgyvgX8<9E_jEQ}ZfI7WlQ17h`>I?@^d1z2|jz^)KrSJnpB;{c40XJU?hK!7c80N6GGc69)ldr$17I-PprnGRH4Ll~GU zzT%$P8=xwFo%XESC7X`;F=5u(fnqC?5La~u-Vk6^CfJFq`f^uNeNK#8U7H;L6T&FI zVn&@4peUYE>uQ&581)%p)H?#j*2k#NHN>bjRG$;0(zVI)zaWg_D`r%mr6@_aXB~>R zGmI|_vvRiRpkyR12J@bTuhQ47v~CYDEJ4K&IsmhCvO5LX`UZgACBVKG0Oqb6`l(K* zmK<`R>KlZCbpu~)n*m7N)QLhx-8{kwz6BcVD3XIbC38>MA zL47X(%DwqEP<>9#x6y&3`w0U#A763vjaiD4nr~C>qD}MtSU9!WmRo1@JsjXvLi25@ zjVRE3j|!-XhCw|R0Oj6%Wvb7q`QGV3(c^@Hn~$%!`OdWzB{km#4n@CumMQuPVb+DV z=sH{Q$pEtwTJRzVP`6Gupq>&??`{~>?`wm)*a4I^lb;b#@2MY@|3~_oN1SH^z}!Xa ze5%t)v@UU=>N&!|qQzG%TJN<~C5hJi9g4QC_@XfEGFx<=qV-aMSqY*w=>W>M;>!Z+ z^7=vff2Xgx75^&$%)J%2Qk_n%_)iX0?I0V+_=;QciU3vd7U>5aO8%hC!QLQS(zXRk zuFojlnaa?M)9%*$RaBo7uda5WXjkDCUoo#f6rd=cSJyg}e2wsGFX7d7fs*Uv)jkdJ z>KdxgiB}(SplCng6<;y0K58jS(heVYD0*KRcn%h3{U2L&o!a5h0J9Rb!zUa-@u|(M z4G$AgpKKV^;k7~C=m3fjjb>0s3aC#t3~EMgP&YY%vM!LB0_xNCgYu82uUU__)&}+& z2Vi_aHghW_z&_gmusH&3Zf#(na{$KYb2G4G1=!~s0A`m3=G6vvvjZ?b9-M(4FTlRg z0I(D3Yi`Q6+Q4pc0A|PPlLgqV4FEeufSpOtXsp2bE_FoQA6+bZE;ZU+YTD(j6b!VXD`Ub|H0I%GQh_6z8PQ3aT z2Z~-t`|X%le8s%F%TkmyFn+_K=v}4g-DFGP|FuQC4vhZpvL$d2x-z?kZ`&M~VDKIX zV0#I$y#?3>(dY!PSBHL;RA}HaTecupB~u=!I2J@rrGhz>D?PoeKJI=y@Ny!$ZxgKQ z$iL%&{6Il|kRZP(iu{NO@SbKqyL@*`es3YxS+o{iheHTDE2nfhW$)| zPR3FJct}2o*a1WYRM6tm5Z@M3?ypVC4;@H3N=P|cNVz0J%IZ(-IBnYERZAD*$e1mh z0~&z`I<|wO6J~WH>Y+TbG>agDA*3v&7(9w786OxxKGVVRVWFN9HmXF}D2qSxs8}wH zxD@!L1x-CuK15^!MTQgxhQ}$=FH>mh>r1E`(I5+OhD|WSqOWgx&x(~m*P=o`W>SbC zN?6{$vSSI|Rsl^jg8?r((=;cxRz#`(Lm?6>IB7=7Bd(-S5OPhT7!>OD^)2jN*1571 ze+HswvPFsk0r9C6UF6!@nmqj1*QesMyExJ0a8All>7IhO`SdhlN6ABpM;(Qtjyj4< z(r6K{W8&5l`BMUeRu1LzW6XMr*io@7T+Zn$db*_+mG-iwuaAP$)-*58mYUb~_07ch z?cA~yE;8b6%4a|he@!&e*)JsQR4V=jWhrjeTozH>@)o^`lu-0yib`kRaSi%Abvyh4 zebhfbya54cC8Wxfk2N$0MRu?BpN9hdM~&~Re~0wFy^NToSH(I}Oi?Br&Z9$~I68(h zNt$G}95_TlzRmJVjcwqBjmXq z;Z^b{v7a#_o(d_75dq1^Np)_?&?#T0q47Ez7Hyzna8n%bl){-C$kwid#9C%@V~-*# zvfMJ*yn>Vg%Yb+8aIS#3xUgum(sg_Q_8i6zxuvD2?qCzGD02VfwCm7}H}8 zMXz0Hir$4P;iCWD7VT=E=p~!os0=g0$6J^`a{zVobOUO40rhypp!N)a(!KR|RDd(9 zAx97Z8d6mNBQn$BWb07Y)65u-)lwZ<$MoXN0gB_z4CAb{0v$lSAxDvnI1@XolziJr zt~}J%_pYRLq0*mADOF@C)f+iEI;DUhrm-lPpe;<^S^b$O3Aa@kVh|Bx0W;9BIAs&L zT#?>5KSD=r@<0r*Ix=2pNP~vZF`FaEa?Df*%S7lV6agy+I(86oogDOs!GO;)!T3x{ zb_K^W2O}Lb;|TdglpzOwdK$6H(| zpDV|Pj|!Cz7Ow|(#eJNlplSdbm4iG&B&7wnQPPGJilZSGK#j-Ht0?7x$`0m8o-r|8 z%s?#@1J1}zKs+dzHAMF?Wse;|@-G|{v7F8~qkIU%bcnio8^TDrtyzad?J@a9K0=dA z)fFq75Fyh@fItnT-aw=!hra3ziHdMTQ-tECh&rYu#8Ae{YM>#gh3g|qLadvAZZpo@ zMy8W;VSJ=NH`d(dHMe(k%$hT2ZgZ>GjBy1Nl;);!5_~x`Ow$8;@wq9mPTMxw-Uk-n@CU=FFWl$CTitd>r}p1P#M*g?~@LotS|5?0rlI4L4B|` zsHYr2SwG;_0_u15gYy5GzUFD^HMN2L-T~OPR16Qz*9owv8vyoE0rs)lz@Bjcc7p)> zxB&Y@1He8Zz&;rS7N1Syj}E}>anYX=VDF308V|;-5sE>b7+v(v&h>lk%a?jQ!=UWj z8JZ786xk`KoZ<~5zbsM`%=c8*s&+GAnS8TKx1}(XqW?-rA~TXnlO~$q3?%W7CLP1oMVrjxlY-fRD!y)@KWVs znv}A(a?cgrIO8o2uxdYo8hM9GK==_yStDT^L;Bd|;ddOOIRCYuC2MK97 z5sEeozYep|ND2?}$XcWXN-P0uJOzZO%~Sm5)0o9JkZ;u73u9ZT9h#BfM4ysk#gG%0 z7^5U8b!3{b3?|fJ$_jtOqAA6}+#1WXMG-t~#oncvD;YHcTne@C*e};`;pR1Cqk@4?n31Dg(mC;LbR8dBxmG?&N-7bKgg%iysWsM>|p8`TE{dmEv-Oi6tIrWpQR;qt$cP-ajZSNDTCZV z*+t0qfhW20K#E*1l+q2hsJ}G~>aN-%nq&ZT~=R zVACCdSrht40&J%SfITF@ejEfAZ$iJ$0hl$R9~NMjMos7y4VutnIXX(Jr;uA&q_o1p zOt!{0s$oTIH$j^Hzom#NEu&#Jt(>p(jawn%MR+;nO){Xk)P#3oeu(iR<>s@^Jf4J2 zagaGxJG?57f;M!eH6CVB@ZDaM6foP#Yg}Yx>$l@x19$9u!k)M{bJ=z?8w!+}&j#P^ z$iO3)#eoBb!TkDhcn~r0l6^iE!}~BXyx&No9cy?Wzf*i%%Bnq&| zWr`AbIbhPZjF*sd4+o%;V|Uj2Tp@=Z!e{jwhpTGVgeu7EEYl@BVfn{SZ1M#x3c%%y zgwJ-KL{l7Ak;&5!Jn7KKGt;4>G*#hQ3zqI_@((S~GZZc*ah8H36$8gUZ=V^8!(!YE zWEnY`{7nvn@6Tz-Wy6lI*yP{ES_Mh|;N2aHwkH3RGOq7oi>}i=dn#yb#GCwkI)J)S ztn1$ksJ$8n^-OJ0dpm%-NkIKkK<(2os6W*P^=1c9&rvZf8GjZ~`_>Q2f1bW(L-vK* z!1i+hW)0aF1=#)#0DDP*{VfPA-jF@O0hl#pUlCxJMGe_Krew(4qcNsn$zl*HBYk+p z>QwgY-1M+A0<<4l8*8!e9us-jl>6G)r`1Uh+I`6eszw-mS=pO&1D>paJz4qUF_9Nz zN~G0ZByb-ntD?2nnBCUKP&QvNry6ko9wAofTSzWrtx$yT!WdsX8l^}J^*j{`mmmE+P(CeRb9?HE8~2PhkJ?pC zEO|#|GWS|cc46|`2KYidy|7h{%oR%1pA`#J3J6!y!w&Au@c(ve3#M@v{`P?~_lSrw z=QVe7i_PtZ5xt%#P0N+JH^H9g1Wz&-MH@5zd+fB`sAop(wD=R$4{;b2>)^n(1S2my4w(m+$n>M-oQCQ7$w46J$iB@t)vGnV;6pG%`**zRvU=FY#BovE414?P4Fu+?TGkm2G=4n?oEwC%E zXbRXc%wV7|7cpef$rr`Vyc#{iJ2l~^nUyKPmZD9oC=~lP`}(*fgbwggNjP+b9ByM| zRt!(@=5X3iiF$Ah61f?3d3?t{ImAMs6m{GKHu-DX`N#^2UKm4MLpwnYKfnsOS})&R z93RelysL9Fol>UR4?u@|w20p3<<}RAV+d*h%Q+&$?38!eR?9@CLRIr3dCf91`C>}i zy_8~R{6n!Q*E5DH5&L1fHwcYwCYv?^q~mrSAPxquRZagDz%{-!YrrC z*ku-dcn~WxJwH>c9wx9vmhmXcNAGOXADMoKuF+@w@#@T~4vs`IySZrx4rx2AV`-b( z6r|p^JDF(#0taTb7bf6QSMf^}jKZ-iP>6A-Ye@u5^AU5KfzcZ;ky1vNn!#!M>LMQEHyj8tR0a7pIa1q#F)YqrC@bzQY4iw=a9h-@rFxbgn!b3F~ zKr{&+>3VgvfC?h(^JaVchMFxv{k4rLg&1ODbnhd_@u(gkFk3L1peX~9EyIYf!||4b z6nkTX4^&uNOEW!%8~@y9_9@VM2<^ab&t{s>S34MK%Vf-V;_&k9PDXlFc6cwy^!I1h zoBdAoi2iFXj^)?q5kY~5q^k4T0P9e*0c&?P0!Fnkt*u5wvNhv+K-#sUA2!D5hjw{-1O^KO&|y`-{1^}J&yhllc+kQJxnWi}y9Q4d8WxH!={jEr|6cx!!b zMXpR{a94h8tcd97zCIl|sfa6KEI~{b#v%-_*uIGlMjlkwswSS7w#*|06qYE3z&5Gw zHe2tcxP%g{iiIXt;=0J%MXhPx0;IG~|Y!Ii3(@qBQ`y9H?vvQaeUp+y-nmcXRNiv8p zb0~U~7{n{2SANHSt+Xt*6;@$$Xr9j@swh%koiISZfe$WyccPk z10d^fOADZ@>jAXYcQ?;Jm1PL1+6b=Z*3Uu zWJ_+#Kw6oxFxjzH1b@Qa&Id}Xzsmjmhn}?EA!L2Bk}R!KAEBX4ozhGvI~OrUbUlz1 z%CsGh{D7UWvkb6B1ARAE97f{ljgMOR)dd@X;dk zKAb?R!ly95!8C$xK4t2|*w=f+bVX5HAHd}GnpQp!0GEZxB8L;AnWRQLI8au!qe4j#MD*hj_r!d&0)^}g)T7R|1>Mm2m4~q zj(~e*VR<{d^n6_nBQ5B}o3Cks zd1{U-UsZs;B zFBn560c>zZcAh{1Yx3E-tzJHR%(}`#`ubKeszAyg5?EE-@(e}gC6={@5$Q751&}2c zT1!HWa?=+EL3dLT=0pp-mlnJd~1HoLkhyXLi5gBJwg> z31`u!UD?DF@lr}bTtcH_zBVZ43|vV$i#>}ruGXRXq`LZh!F|#T{?)k`-0RQ_tnc7u z=>^}7_JV%#9cynvW1S!%V=CKKSp11o-qAEYOmn&>Ro8q90EQ5oT|zTW2g1@5X0!{D zG@?uyu$0_;Di)EK%KTzKa#g{R3&q?I`zXb&DP-;!{>BW8ofg@p+ z>O={k7G;J@gi3d2O%5e)QmVAJI{$}tmIy!FytBzYNL&r@l(zo#^+9&QNlft({fx$F z<*7O>V7AaKw_)v()tyo$F)N_nxgj+9B~1OzLG{DPk)!%_1rp=z=r0zB`|2217Q1%1 zqnL!CIJIGA6)U9knE`r)c|iKsgU)^H#}0jKo;ZS=Xk*F~22+n@ zy9KEQvXsOK0kX(^rNVJ(j-n?I@V6pu*QOk~RZC@f*i&d+Ha3j2C57T!s6V2yZAskD zBnqa_jO9j(l$ZwI*^Ds4EmB;+Ei7-}1V;b^gp7c!TimtfAag)NDSC>hsDHU%DiUTRX` zv%N)D4bf9!Dm8Ux5XR5djUdj2hYE4SCK5G*EJwp7ovkZnQz-Q{L2|IhF^oii=yYUP zpqKF^y|1rX&{EnhEJgCh4%}CKp&PglcP;P!V3XxFoER{Rus0VuMROygQ`I zqhQY}*%-A7R~;pOI?&gY8Kj+YaZXvCu#{Y~dM#SZ#Y$e$Q-KUq+9(q+BK{%Pk&b3k zTN}x}p;PD>ooy_bN-Ie-0Gr)lJ;s<{1qnt72A`q^^@YlwXd@QPOA{IjZI*;zBsU95 zJ4?t4v|^VzhuAFyhw&_){WIiO&2rYG)9VOvhQ|4Iuuj#c_BBC^sD#9dyl@U;)&0N{^%ka1I`{FKIqTS2z|`q=4ZMcr#N>Ne3;$g?sH99?Vd}Ix{hW z`%$G(k93S+z*Tq-^J^KqUUDA0UUnF}PL{E2zKmT@M8~eyH<+=DGh&;q5Y+1vhAmG! z57D*P=UQpsE-rY7TCqSDc>Ey63P6%>EAAsr`T|VL;p))deLbLI4l|asvm2G68yN?) zvoKX3Z4K1tVNmn5P8Dfw4>t0~lA-6OfON11{-==gw0`^?50^h%4xnGGvr8HKPZT zwvN6h8e9*$W>hwQ1)0WapU0F(bxym}k1Wv@HIyKQ0WR27^GeTS$$UVi{@3HLl$1CC zLyX(JuFRJF$oPn-vJI2_M)xg}Zwx)-V+Av!^ZU@HQKm$7nlPoNCE{o)P=85N*TUv> zoSwklhyL`Co4HJf8=y~yErO8@^z8Z^y=2GhKrt~?a7t1=u_cFXUmsT5v)EM(DdcDK z5ph7yZ}0^c5O||klN1s9b8>hZ6E$9sFH}boY5ibLduS@OjrZR3+P1FXo)I; zxHcv)Vfy$JAO z83UdtslBR`)4bDoU@P~9BH9ODGBz*YvVjHy>}XaMU~ znNXaB8)O@^-4a+kgJx?oaBBI-lu@lgQ(^FT(sa71#&jAU1dXcA%)HHn1BdRYWnRD| zV_SvI>GG)SPl>yup=BDgB16&@7=i?A?kivXGju?pERr zO`j&a*J(r3J|sLfaw=Xc7BXKwF&&>AJBS~(WhZGv)BX-c+YL=Oi1Gbg#Q3fe_!RV` zRol??ymXj@D?3cWhNeRuNU$54J}o48^xbQ*q3JLOKz2jZ=LFCT5uUC-4UMRRMOgXH zsid7Bn)*gA!5OSgx?D!)%jIk98-Z-v^!%s2qr+J1}F3=?fFGtRLQxq93 z=*DV{%F>!n1G;5tU=g3RV%Qs}65J?194twyk%G(=VsXS!{zQb&W)Z(hz7;Vq$(08s z#L=#8edItf#VhL??x8MyV2WL347mPyv5~gF;_y7o`PcKBNGA+iIxUGyB|U7+H9niE zjTcI25NV}=SbU^|-8fL!!%-pQwEAwx8@c7dYXk$3X*Gq~*Y$LQoj;mI+RLI(g$k6Y zADCs+070hQ=%+EIhZB-78ONSkOvT}nqu4wBP_+v=d#Pc#QIs=~Hz^cVi~(&f7|ifs zqzzrwdNHuVbp}CISHwZR6!NJ^Gl!k@h?U29TG152p&xU4b@omLd&6&qA&U_L?8cJO z>%y;yPMi~?6XQ3+9pj*a>=vMVL#^OC6g3E_#%i^ja}|Ct>(0&I~`zf#KGJ_>eICbrFWI-a$6Jkxe-ZTYG0Q zu~6R_H96@*ROTyearHrp=cCwZgL6k#z;6c^9A(1Po>R59fQ$*TP9+HS;P@C6;~3N= zhE0rGP?E+jhx>R0vBns_X_XmeEG;b`!&*D~4komNv_TDxs&-1?@IMMlrZX7(`qb15 z&ycvl$99=glRD8LKM-AaQErSQ%m>?5tjxR-qOH$)&XplkKmKZhanu#d2lTyrhj|WM2b{$c$lS9cbAJ+<+dV3CUkS@x)U=qA%t7gSIojQ&=&uw? zT39ybNK-YbBxO=5l?`k$_=iemhl+_0*vaH4CruaF6B&5Q%vO6ojlLDh(F#}8WDUyz%=N^?SipKnQfEj}f=xc0OtO!Ye~XPc!)EhhEtgub@}$$i%nUX7E}hC` zQ|MPzAAYqi5Ixn7i!e|sXG$`8a-X!i>%MypJ8SI43X+|~%~mJ~%emxKM0z(-@a0rh z-Z)GlwSGQ8dc%-&Z}4mP27ea`_1#rMeGiOwf<2@YVE%41;s)tdA#lmPAo%WUCx~5u zmwa%^f0HDh8>YP;d8|&4yYw6)(o=Glo^c21IZ&kMAd#L!BhoYZ5jArSa$#0X+ z!Ea(B1Z${wLYY=gX28ssA92D#XwK~mBnv!y|1L>grE zKZ&bejX^38SiFYB*)4R?X#pGV;FRAy21hbxQ<$fk1v2G?Ps931h9~OfpG88p4Jj;nGAk;1)izdz4&0?C|cyfcpn>JVX zLZ8OfV1VFknks~-<8?fmgC-DSzmKaW>~&ojcfw{I4dKtUO&oNoXX6gcgjSLRI1M)f zxA=6N=nfWgiBha8$Lp~Vh@3wf6BcF6b5oxA+xk3JRGn@&b8e2(kC+Ima6Nt79J(_; zW{QixAIHP(V$~VBLb{`I+EYV2GGH+4KQVK|1`U!bm`{sQsZG0OJp|GgL`SodCL*&A)zJ!+tY`f~fMZEvhJJSm~m$aX?w5K=S+u7A~R%d$I((W^AFIYs_NIR0|Go$rTcZ_J( zYb!OY-p0@b1-O!zc+5CteWvwpXW;%%`=)bS5OO+o81@Y z2J_f04)Yu3Vn2jB7NFV|3tn3xV@bO{?AjYVBweyvqZZ;gv#?b=<*`fw8IkF1Gh??= z0vsgCN#sR{@al9cZ;ke~(5#yoq1=rWX+vw)+L=SrOu#4bH=KldBtpY}7c3Iyks6u( zhUy$(E@At44{PS`G5bG^+&XRl*q6GJ)Ao;#I2b8+P!vD=dHL!I^hSTaHByqcf85|u zwB7!3x1JuLLYGahj}nMEkw`x6~GvWitoaXG0osk9#SE}c-k zv~d{za&qURqazdr8#$1|IeN}3Ig1vmyhXh| zT_&HFclMIb-cEIYzBi)-nyy$X&S^dP=BEFl(&#>Z|Z z^ndiw?X1o3NeAw>fllG4HAzD!C9cMT z9Ix0Btr%}IhD8|YlsCj^cKQ5Wid`uw#;W*easd5JP;XcA+GG~sK+9lifza0~r2yuo z@T73V457skH&#G4g#tqPbYlC;VIWI6*hjM#Zp)f;C11K$@Q6@HGIovzL>_h%yWV_0 z_UcoBf*MIMEM*2+JQQPs!Ts66dPayNb<<*upw$i%Ni<88t$O%aES>q=F`YSn7ut6m z`i@PN_q;1ibI32s1Q_r5&?RU zCfV#aB=6Yoif4{eS)8MB3dwE&cN)6@I@J6fnq|Ym~l-ZpU_yEIt{S!zR$is zc55g+vpf-{cLbp(I!dY{Xp0K$Lhv4WC_&b;G&zm=I)||O><^*$P#C>{9!l%dBPZN}u zlEtvnqBMuP8_o~ltVfqE59%`MK2cp3sED1f*dY@X!l0*3tG#Yfdp~kkdk;CNJ-hk# z1EThNqH1q>rOBWiOi{JB0w;Xa&M8Tz_*y9@mB&$P3u;5^U<#e-SNyV3G76%>g@~JQ zb~fGrRVTLHU%QP|f;t*%ji-kNn}gYKei_B@kj9Io2Gr^5wA!nv(y^yYay&KR(}sg0 z2bAi3L={$p`b^Zc?zD;K0bWO*S#Cu(6IA&vYXUu$`z&`nzB;x!p@*qP2=*{rcs*U} zuBY)&cmR!aBzS!MUplgQq%5MwJruxhwf8`s}{F_7o(|Z zxjH4bWCFO9Q3OY;_C6?mqaCmMp7zp0X0Z1Q)@yFG3~M>6ETDyFL!|U=MKo#V!ss|# z60qNC84sb*kUc(djZQttX2!Bjlmw~-n;p=}lY)ulc#cL%6@{-2IS(cXOkL!$L^wW- zx>=%5d)Iq)S~G?)X8sVDuNC-nhoTYf(e_DCnUza4U9d;r0r0SjD*3XV%#yGi6tCA zUez9S;w@zw7hxD;dIY)@Z)_%z>YD^OfbWYW(oS>!pF4~QY1x{_S3KwMw?SsU1&9Dvy)JkAqf|4|RH_#-^xoA5aYK;Ol@Q*r770rcnB3eaC10DW5ky;}f1 z|5^ch!2!_S0_YL}^q*4)6z@-X(E-r!us}J2ALdzm5-BP?7j(dY%F*(7$byRG zp5)w>idLbr@?gOLvdY2L`mowNx;hgM6EUB$iHz`Da(#u)AzV=Gr%$U7#DV?h=7gMe zGQj+epLcR~#hkr|&V!;0lqfcSI#?4*J82@qPrb@{EM}ND?GtAQob;r!v?T4uSaS32s=mY+z1II%>avKvf?Pb16(QFUCc>DgXM=Q zcQb7|&|;#DUJ(GS{BsrRn49CaY!N_#cwCy5k%J0rUxW0&Q&ID>}AWXkE>vrB_9#$#_A~T7GT%5R87uptPrwG z1x7e$7zs_@LaTiyyj`^(jxK^BUc!c$IdcL%hc<3y3S9ko!LCDU*BHls!@4&t_7s?*vxbpfeAtK;pk>C(Llou4(f%w1$ScPOU6XOm^DEGV zy^g`|z%E;*%Xr1qxra|SV}F$qyFl-V@!MoX2~2;iLJ8=J=YOEDE*Q@bXVvaYDjBYD zqynI+8g&`Lh*84!HAx7@;sK$j4LdlB54yOgpPaHh+X^mz_s9T{!e9gMres}EdKNb+ zDLcY6X}Mp7wuDh)tT?`Y2#uwtzPy9Tw4B;yq!%g8#-glQ9l+Bj6K;wr<9ZY{s*d_A zHR<`IhA3JdLW5zcpiZ{Z5A~2kGet`U<+L+{Q5LNa3{V9$9+^cX?qF)PQ=WD;&jMqo zujS;z}g#6=ymH@Tn2Ov#uJW!_DOSD!N z-VSCGnX$$QD^_7+cov9<}=CQ+Whn{C+_@sWQKGgl4P5>}Y zR%|Gs=Watjwmj1Ozw#Ji>XGoZRgU28|1D>U5k*Se8k^Stl57E|Y5nU+wbaPVb3M6t zd9uYDU9nAY#%K%|}4-z-G# zN#9LvS|2~b*vkRXtk3829U*C*G7W*2JF*eI+8q+@#3m_Y}*@&FVzS$d?~WUXUXy^^I{it%QqTZ$UL*CfbYfE-3Qel9P6T_f~Alj9||wj#05nGTFiD zu6)H?skQG^bkQ5Axx%_gHP_@ZEJk*S;yoGMcFJQ;6HIHftXOzPEwOoeXxcQy;m-H@ zp9>)k{{B?E@i(o&gP9j?JAYe?e=lC`_Q%uHrqN&b;V+-Ux&0rlnl^2+H7VH53KC}? z;XMr7lxp;Tj<3H@Df&nEUMz-(a{5#12Rf}6pA?4d8BO4}G{++xlk<^9i z9N9r63Z*3pZf!0a7QA{^$fPqRUf52WLFzSTQyJNHI{A{?SiyU82wcT%Fy>309WhID zNnWvUQA_Z=FD5t#*2}I$B{d!|#>0xz4&qVbC5x1TvLQ!;Vd-RpY^>KxYC^?zd)!NTW4_X8_=XsNsyg z{fE>)NBRRt|J{uk(7E`47vz3ZkO^pIzT`Xt&# zOhWBjQ{XoGj!WK1B`Xm&vN}PGI^`@a!5LF8$tg(X+KyAS%=4!a15d@fv2F2Nd~&I6 zfdlAeMq;*$ z_eh(Zs47h>E_FC^$%3)zpZ-B;Hz$swe)~u`a_E8%juIiHIXO*cAMt!s7&r8n`4>20%eDk zJg-cwG2{;bjz7e=%=B|(Ny+KKpe>r zXG%kBdvl}3(%O(Zn??F6u*^AY`2-f^3u{Yb1K}6f%8P5QEweUj95$)ZiEaMFsM&vn ze#ih@w{3Fo7;U&M<3CCWXNPb&6hB^TvdY#M51)L+lU2Ub=9GUQI8S6nn3S%G=n|F@Np`yOioPPWpM5P_{eD?9%wl9=1b&iEa!E;Ta-8vG|yWFbcP zwouH8wGCtZr(%Tpt<<=YMU-EnmP#>5Ify~TKw~kOP2UB?phhlXvdH{7^o<(v%>?C< zSlOKG04idaF(|gnjM&F}3+6c#9Wl(fXg18;=?%>1C#SacLaaHzji5&wHGY}=WCtQ6 zHXjo?pT2V^k{Y!;(xCBNKg9u1#MWa#Z0iL8t$q~7aHq~Xn&Id*_N50F%XoI8rtet0 z!vAJplqhA)!xmx#?Ru>AauO}=HBWR)SjAr@0I!KW&2XyH-ZHi8+@$W(vsJSJOoz(_ z(dd~U$9_@XNp)UKAbwB+>M%=G35w%F<#fA!8zt*^}6!eBgrt1k{WDYjD^5+an5E7Vn5Dj z9&4w*dLcWQqQl2paJp1e`}*}`EUMdwzD$bt=e2qrJ*&D`whW~7GC)3yVDyqVfF-{G zc_Feh!6&EpEbUe+I|F5Jz=NkFlakm-^2eo=&u;*Sv|>?aET0X2y|}k$)pBByLSs1O zzN8opla#rf-w~rD<5g0pgG!3b_*f1H6AhboATImLIOy;O-PBUGGBOW1!4SHtyQ@2OQ(e`mC7lLw17`@fB8rNlf(wI;A|Ng(ijI!kxQveDGLHK) zqmJX}`04nmcRL9wgOsH^A+Ss)E)L3r z26=4T{3wjAqY{pmGG)7M5|?X(0X!mSO|iAnCbNZ!;uKQhs+XDJUe#TAfjHNDc0Wc+ z8UE>&ZnV;1HtxVZ!;xDZQSFigHl5gnE(7U9P*!VJt;?{<7^PVRrD<7{CD&vf36!l` z-G-A0Bb0*;Dm|pkUa1zcLW%=Db8R5s$y}j|93zr)W?FoVl(N+zFa+Awl(yf*bE9lk zxmNNNsd0Nsy#1_Y?IK6Mz~8yM)1nMyjOC`ziJur_b7gioHVT=XBF9rgiK%+Z6xgyZ z3FprwoO>mwqJR%B%f<$LX5drKyeRLwUBU}G?=5@g8=4#&U(g||F#N;X4E7l>U_#AfihlB8%>X(x2Z4;*2HqFNgaDq-q8 z&(IUzC5uHXaZfF`W>M6gqJ(|BbhQ`$A`TTMkQNK@u$E%*Q(UAZ|6%&SE$sl;g6bgipzdyAzz-HHejcTjiigv=L(_cnY32 zo+gXMv6hLp!9I8!;jlDF$X)n&?l~Ds4lO@mIrCcEt?kh~S|-oCSrU^D15>Tlssy_* zwJRdBHtoe4V-E31ch0JvAs&cGA)3GXnwsuUXSFKx@S@bR?Zqi%+n8dVvMm+szHBRZ zwO54Zd~IU4^~&+zBPqg_+D`p=-1Q=I6X-p)3M6A9ZhD}LXD+irNB}oD8*IGO=5z=0 zFb14?h>GRO!eOktu`RSJ_r6VnAl6&|v`*#EPL?gv%99lcn6k)l$zo;P+p1dBAkcr# zar)P|sWy^NYPGUc+=W0Cu z+V(OAhfUd{C1v{z-`+=gDDJbJwRRnD!!NboP}B|{o1@CWx_)ps_&+*FMa&r{V{%YH z69#G5KSwWPYb}b%Qeg_4@3Lw}cz&x4JTN+fiNPCOt7J@Oix?Fa4aaLTJ1B>8IhW}| z1|4mZNv<}^DbHlg09E*HbCWS&X;9SzXYLBMJuG@Ik8Pi+k?|A)*HqawRF)CD4fN9~ zL^p$0u^Z3MqX&_{B~j)ox8})er-CP%wltBisn|_OrD3Bb^!AnYvJ*4@fgQ6ZXg^a% zh>OymK%5iLOzx48B?fALahF60Ole-}WtH0&pF`)(QO(RrDsBV1XE}`#q2aO+NuSxw z7ZQyvdmY{BUSbN>GKtAixJ;t&Fv>(9ry4`|ks23xV*Ab{*i_D?*l@$w9WxWw29Bj7 z)X2!TYQ`&dU{b0Ov4!cu%uoSIP%7ul8r7m~SX3zb0U8R*!MYt=7*3$ZG7FK|2b=iC zRLu-=3srl+skB$(eGgZ1Y&~RzVK3ey+5^s$`dzK5$Qw@v?cc37$Kd+jo5m`z2anX zY6?RE(^WU0=#Kz%k+O%?#^1M@W10jGwM0Y(4lkoiG<8hh`f4RV3H6+cJYoW2Jw3p? zIIMNXD}M_cNzfHdeA7M^hxYXqv1zU_UISxjR7;eb`u8J@sy4K06%?%EZwx!hj51R! zvVt3!%_y%k&EqUrXgMg;dh=^oCYI^`1PL=*MK9TiR|zD6!f-c9$QA=yFGecW5}|dm z^@O8c$dzN>E~d5{$8Vh2_0 zLhcmNNh5~efCxy4di1RJM(<&y%ZRZpd26mlgWiDDG-0ATxzl|j9q zDkoDDmsDfNU%OCjH{5S2vN2f)$ndgePG_<)fgwf1fa1bMXt`=J-URLQKmdEN(DysBQC_s$zQ3H>nS8%S1(D58+-v%c1D9_PRue zNGZjnhmu5UAHPxE;$oDt&2pzA+*KQVn6GO!o7fB_4 zrbiO}Os@idTeBH2EHX;0 zeIU{5v?!BD1)cnN;xLkbc6XJ&+W!&J; zd!#D;V5;z;M_VWT7dR(<6Jx;eq0btJ{KW8~6BV(Ol*j$&+I!sF0IsC)p%V;}g=eR3 z_h|dJiyAt~;Or1J6xb$ziop}U$hC|YsPlKGT}*2+Q0I&ByI+zf`TO|Y8S!cUa`bLE z3Ww&Akw_d$Mc(@dDPjWOeLGO`d#Jo+JlMJX0#h*sqKkDGZnRntYNv zT%vs6giDYMB!@~oBHH^VO5*8}(UTk{VN2~8Awhn_Dac>k)bW?v)MdhC*Z7YL!|D(r zk>ncx*YcgWUrgf~|1tR{jcfeJQ-F%G9M+dC$J963Bl1%zigvE?WI8OGOecMPm(Dfb za;ori0zH*${1;MG(z(WypRtmEC*P&7WO~>5FQxz#V`v0KhUNj<^G^>FgI5U(G^ikC za)?)rp96>YluFwPs00XJa;7}n?1Vvi)V`Zn@6Kckvi!#NJBw_ z3C*jsB!|;SPtjS`I6Bcmbz^0sF$1BU-l-;)lup|u7V+}(y0QX5eyk(+3N-BFDy6xy zsG`jq@N|Y(oKEqkwj&XEN%*19-!Df_i_@mOT$``)=q%ip?o);*=pVURf-g0;7JN^H zb#{V=!Z&3LA`iq*f!Xh*bkmTEQW6G9(=`<6p@W?{Os3N~z75?gbwZ2y>C14JmUrB? zn||Dz*xx80z&&I`!Z(2wsoj>4PD?EH;_$_nP=SUTr2<#%XRHT1O1zT{UWLlXD@FK@ zvokR6kExJ^O~%%+ZOgVz0YfB!g6*Xe<(P&H*!5=k;t)@mDCo+#YIbtUcD|UNjJum0 z{5O?H;DqSPl0K|V)C+a7G!^nQ(gNC>)I-;<62+-87^(_5FcgLz9q}*vU%$;6k*`T_ znHrQf7~H1b*VaP`7l7FSb*5pk*1+B#ZCjLaP=E_XsWDTAI)-ITG^%25fXzsSR6wOt z4^)~ue4j7P<>%oNj$QU#6&i7+cyYuvU1T;i6^+%vR91PDS{q>N4Y_xMlS^Jkf;hWL z;OpTcdGP?uoCN^>X6r?N6=fh34VFe50Y=R=L2IMrX3VycN`12^FI-kPp=Z;-DK07# z$9cuj6bSa2=uTqU?~aX|`LWSc>Fp_JIJTz3q`lY^ZL8^597*<}`i^)ZvNCdyd`dRw zRpxDS_g4wRRrSg$QMJm$tc+#%p&f}Zw!{X!dI_6xjhq%Knf3k*hJG(yJqk1EB9Act zZmz>RCg~)%bs+m~E$9M-!#lzCE-B9H;yUf7eWBu2Eqo0zMF-d`cER8s#TfY$nd%hV zv0j6??VJK_LwXK>kryH(ZYa=>e=?nQnhYzRhd*eRShO z9HuheI2}8UQtEs@Aw8d{^XWw!(xs;7pN06nq3NO1xz_YN@H-$vMF)RT({r*?fqH{b zBo&vpN*J$IsRlark>0OZmBX))&PZh}nVZ2cZt3`o#NOYmRk;MO<(bkayi~r37wD3E zxQ)&8_=mM166lfTeUqKO<{ zD)ePDKhk<5A5T}~bTP!ohPm3*W8uumZaP_0$NtMk(wt_|KQ%-F#1gnT<;#`f8GxD4 zJHM8GB8XQS5aFxjS5)Wq8r;&o^ICaIea7)hpRqkvU%lQIf4%&Q<$hvYyb%w)z8VSN zEA4+{{4V?Jty0L_aPj-=1M>dO^5pHYC+x|0$dh-*o{aY4=56xi_Slnqq_jKbNn#og zlhH?Ob%vBm?)~z9YpqWBcuY9k4Q?#3rtF5hr2rYz3#a!u79_s4!$1iCOy+$|5$e#e zg^A`Ki>B9+iL}5?fWnVTvGLK_0Terzpgv|nt?#jkBo4SygK!aRsqk-l0LHOJV4t+W zdTp&q91{)Md2!vxAx%J^v4DnqZK+D5IuRXC;b(hjBu*w``n<)op+`)`+8EEvmXmBE zEyao2?YJ5|fU!tqU$n^1>JgcEm0@hn%tm0D#0!#4L^0yTq3%|=r-D~2NMAOP!oP4U zoZ-!~T8CeiH>glvD+^UC){~0vb(ZcoEZrk5=zh!6J*_*sCJ&HiKjF#3icXQsGv#uc zT!!RwhFs2+%UN>Sgp1{z&S>*&+kM|Nm=}8OzOk{1ym*=*JzaG9V@v-?%GYW%YlUuBY5c;_Sm>1}qVm|3 z77o{2RR32(p+)t>?b1WB^~8J~H~vP3jzEzO5nn%A_TdCU^gJZxzmXKB8w1bi z-_ktK(tL9Znj=d@^COn#PD}HxEokmuBATDFG%vI?Z%U?#y;}PLlWIx%f+aX+3ErGc zFdSc^N;;8j%F>*cUkTHMBH5y+$+v7RXDr2ATGVoOiE8;HOS5iiz9X6D(sbp|EWt}G z!CR9FhLCI`O-zvbQ1XkN*JEpP+e_kE}Yln zhH7j~j{ZSL1lg>&kxnrxx)Dts*rP4jS6i?fdj=~uox*s2;y|(t-6cx5bUn(VyxyP; z?`+YHZ(5>m6b7TZ@hygC&nhY87J*J9y77^fz2^oAY=gYrfD7N-qCwu#OM}FBsk>x@ zvqvubkr`L2E_dl9?bfGIaAE_fI>488a^it^D?>B3j^ZxQH+CLA%E!{s4mZ#n8-1}_t zN6RlIGDLgp;dtQnme#o+8^249@NrVe@woUsMz!o?<;mk>PuQ1#CQqIidqM@}ljTVe zdqOqgiSncio%?cmzm?+aNF)3OrjOMexzXTs?oXEjsB=%RCXJ7VCS7uZ*6)`xnPbz| z;c;5`ti_XFlNHC4sCQp21vly4+rx5t_meHG^ebc&tX7)$Q+j~rH1E%}(0XsNl)Cr! zt>tv@&$5uxI9MyAUQ=r&Xx~rop`o1ieb}Pflv>J(qv}@w-oC}0{{1YAExisijxDtY zJ_(c)xSR%lg8>!Z?N&U)si~dVEN{q~6#l0yT&)<9-eR_=>$ZF6TDl)@L3f*_`^WC+ z#$}s_eiyKhmdnHCa*SLaDVO8q@@Tm{RxVG(#qv=nLOTuoa}4G#l+f`GY_EggEfiWv z`WBj#gq#k3kAW6mD8HgB?M1kySK58@G`fe@a#8q{XI259(^7Go=vH`>ctdOWwM>9p`=OY^T=&^)+AG@TZ{VQGH61EpJ!=)q6N(tED=qog}>a={8S5?f4)RCofiHoOY<`=XufQTXgV$YHJ0XQThP3A ziD)`4{B@S*=UdRceu-#0E&L6Z=HIoT`IaT3>9p{FX=&apzY^4o+5wY+_aYU#A_@3Az$+=Ax&mx!j*!ryIaex(J?dzOf%)53qu()?Nrnx9xAnobM< z8B6mYlWC?^-L_v+oEHA?EWv+DCK&$x5>?WPWnZ;4zb3yDrU}KeMNbo_h5weN_^lST z{O%If(rMv;XlZ^YndZ`TrPIRy)Drw&GQsfQmZ*|W3;*wy<_}uX{M8cCbXxfTV`=`V z1AMcTIc}PA5b=P5jXY zXZSBIy7Ce7M*AhQjS%hHMwpD!=RV5NTkKeJWDQT%4LIG&XLPDx$Kn71#-#DWlAmw04WkayE);a@B- zvZ#M^?QHeBH%nl7Jr~pKHgF?66u--q19RqXMAxoae0W$sY^Bs_qilbsijM08w{_s7+z&Dr1v_FVUR>I_4zRghUf*$Yb=)6S}f_^gkxA# zXiRz-j)DE-73fY5$k!Vf;s0`@$c#%4$T!Lx)NEba)gW-x-fS!V7EA5lTTr{nQhRznnZLNWoR)fV8QZpQ&)DvrTgSF;8aaFPoJgaGhoxwxQKw~Y zPX3MoaopghI}8y1B9Q1YlkGx2b~063k{va?MGB6y4rf|m7&<%so$?~-o}9V?6y0ob zGFtU_Nl{Yu@HY9Sj!KMG-9o@oyQF`IrN6dY`ivEfLb%gHcu!{tAqNCeyw9RYb9%MX z&FfZx1u!20q3k^2T`2&3P~IVcyL$(K2!ZJcfbhdo7(3#gmLS}eyumtof7E~of9*|4 zeH!~Ea&GV@OeunQR>Y%vf_-WSQ&5T0x9 zCN5d$^$6^_w;N!8Z-M>R0W%1QYs4T(Y?|#oB3N$`e8VF6eG3FT+alQ2BZABA@c6z( z@COfpmRq#<2FJnqJ(9Z0lKQbF^~d(4F6@z%J09-mmef(x=tlMOd)IKP)m34cFrS{+!)Q+~)rhBAzjFdoX$69I+b<_-J!D1AGLd5UkfRHRj z?v{{Ss(*jm7`r%B7@>Y zOY@*dvn92Ns^!Gbi{(WY z_;6P(-9t$YKaTfD!UOR>0Ysjcae+U=I} zRV}qgwxHIq)K2MtH0jke5j11!Sgoj-_i`zfV&2vA%Zny#kLwzX>y;K)`b}^Y7m_I8 zoVqdOf~`k`RKsg6x>t9F?gWeOwH96aZE+NxK1wXwY_GGxUf&h4H&|eAw7`1pPIY2m z6xR(F*Nt7_I?>{Ki^VmRdM_1iFdVBE1@$%y>ZYzh-E2X<-GWN*i4_H94{M7;`zs6W z9bG}Y)k1rhg?2`d?Zso_qOinIQO(NRyMpy@3+qk`>)ak;C7b{n1^HefLDuoUu0Y;p zL4Lr3?6q4D%At#6mDw2IxZ6VePzq>n!G4+SPswfXG0?(C#}_rq5#cZ8)zcH#w(#Tf zF(rpHIxlZw5~FpHJ|$%l(x)w?$HpOzgbxxz&rXK(c?;*PMZ-DW!ugVg^LPix2*cqU zL}uJz@R&{RFUE0qqku^C#0|f0k#1Tv(l^+?{H8_vXAY@>^k#YO90N46?+(9b;cQtn zoEt2hA6YmjG{ZSw2yIP<^HU3F+oIvH+8kCtw{V{9!SQzF;lEqDI~Gm%*HRYg{)eUe zlooVwvmhNl%tG3`Xh_4?few4;SPN;n2gx55$62};cSkpFdD|lb@N?yI zkz6j8OHMB1a+#FNlw9`9C6r4^E;YGaBA3hMa+O@Jk;`l3a=l#MB9~imiEK{C1a53| z>eTvo*6nBK&#=794bS7%hUanjGaDYem2&Hg+sB(5R z8f92g^ypp)Kdps1BUNgH&l zkbjq4lEss%h5Zx4-uA4c6zxP?&u17mBmufSl+3z+|@Ofms>1XTP)9VSWM@KcT341GI)&S z_hGqvkGUJwzrV(!yj`RGjz;+r`6>lpbCdV=7UlI8<>{?Ze$3#Bp!|6J?%xQCPnf$= z>~FK!f1t5r0iewCN%^WZcC#=S!heue?>;&mKrNiJ^Vf!71=a9ta(UrV%a-lCcHx*9 zQ}5<@09$Ow;{lkYlKhOnXx8R0t#A%sdT`mY6)(D0maIHXu2bXUM+8cm2 z{3ky7ru_XaxqL}%6iiOZqWOzOGfkx_Iyn>n!%`IO)@>%NWYJ7msfrGN2oMX;-cl~g zK}5)_E$O(YvH`L^7lQqT(riYuWM|~&iWNK_6FG+)lKVTT;Vg4xva2E$_qAc`{GvxF zdtmVwYuCaaoh))FFqw39tYE8LxjHb;d%UqYQQyn2*Olk@nY6nqbFRvT89r7TCRLt) zNr~90cU1lc@XPEG;H1&=ae zBLO|*-U7tzXdT9{iymdpMgm&fJs>|dqe(uS+)B*W)&Z1@9c9i&f;zPaP+nTb_Mn(O zt^+6+HOhRA1of=$K^gl%dq6`{a0fsxW|a9F3F!3h0r`0u+k;|ey$+yU$S4yw64Y=H zpsap$#3nON9#72l*8!A^6=kkQf_irMpyFMZ%-WfCumd1xrDdi@0%E2{k1s7eAF1Gx zzgwM@Tc}ngF}|GlF$pdzCxPR1V|#Wz^9x97Z*yv-uv-0+WI)Lm%RKuT$26#taQFd^ zA+8q-NC+N3yHN<_A}d2PTa;a?UK#KYWe=N3GTb0-8)M?hjV0L`JB@P}MZxrvY%Hdi z^c(BAodqmtWwwY~^P+28sv6jol;JrEHTYU0*OwHFooe7hU72i>H8@Yc^J{Qy;qXLZ zRyqx5b0eK(hYnz-@oiFw?g}$^lVLIuNrzGCLQa7t_ZW%bf}1no@EY8 zR)je${fZo~C#lG!!^4O2rAABw?E-!wHGJmV=>Xpa;xZj3;WHhk4?k!6p)wv8Ch8(n z<4_kEw!wH!&GaZkFHPy^V{+PvJ1IBVnJ?A~!6;;N=7R|;NQ_^d!@Zt|WSF`sn02T_ zQ@HqGX6a;8FiWT36n;;hxyYWZq=wIYNgd$3P-7-;u^1mT{#Tx_EhJttC=st@WG!q+Oyy#<`MOxGk20nZit^ zI*;=(%fa}3T{HzfZZ$rbx;ji~*`Ycv6rTxkSsfw({M3e?Z*#-S+3QGdZiDPOA)@0LBEFY>Iz(J9Pd==+GLbotJALPd;vg_7wkfWgB&R{;9*k6zY zwDR?qdQjV6X_PE~mjmtw^bwLgqqA-&YGD!#ALlsY&{VY;)cSDfqW5@!M?98guT;X} zlhYL(I=O#FutCWoA8$7sAUS!pPtK*3D&jCs92nO0Sw>bJc~WWACn`7z73ECifqk-f zzVJ5*J)EpXVV2bnJ?t_lFpn{N_)|i`>){T3fx4Z~$Tz8UJ52l90hLRXz=Xv_^|>BU zeIWr=vLc6hXgi>CX%U#9n5dYb*lVkW(>58!ST;X7iNjRug^XNQ1cQTpxC51_{Ao_p zwfd=Exz(4YiZKmO@^DaDcEw=S+CV6_Gv!Oce8{ZgS9@squcvPKZ^)Y+8r~&eU?OCq zVj^U};YVD<%dw$ydXh?0ovDwr@v%lLqvM!+Ha5nN7!N%0kcP=#yW?9@ooILHb2MK8 zHfbRSJoOc4CxbuDfU`-!ZD0>04CHLt1`>k1<1ei_^PI*^zC4J-eJAtdICIv1tQ6(? zHFFlO{mPtz3oe~ACu%ksM9oRF6Y)~P;E)zzswlc6b&$0E)-O1CUgSLFCX1?X3s5J! zPENuc4C6q_QPdgG4Ix{IAw+e*q13k|_T(#dSa;l*O2*cgFp`rE@ZU)>l4Dt`Ig?wY?g52fj%H&CO71o$YwZBa z8Q__`nV=rg1E^DG(Xz@1vpuL|r0@=)oB^I`n+fXJ?m@+^xXG>b$P|E_0iH>l3FuMX z0}4-1+e*i!0Obtu%-Kv($M*m#wE_OoDL^>`Jd-sO)ML5_74N!a*8bQOfSduIiJA$B ziJCpW^gqr3Z}UipPmm^v%1kZRK}pWsY2rBp{JAnx*;vdj?KhTD$%;!tuln%NW~2_E zoKS;gA^a&RYTyj;%r(s#^vidC4SF%apCpB(+N7;a0n-`anG>2YpB{(Vn*n|>0ci5{ zS(O5)Gr%(^G=VZFwBMfH7~o%h)G`S3_^%5+@W}!+)gWiSw+^d|v&1vyG^@mv(|(oi z9ZUSFsVl;qbsZ|=Eb+`^&5AIOwO(shdr~Ox=D{_&vE;OZ?{4@XwJqJHU6A_^a%)!KB|l{CmR^e_rZp zFyCW`YB)YFeL@c>Ykl6BqKBO&{*QLHy-B|Fdbk5$puy@!`6iW;he_W%a4Tnt=aB^* zcW>jc~`N5j_ zEWvwEAB5Y5HQxJZN$v4X<=zJtd5`9wnoa(DVv~)H>ExOAz7%6Rc6tTJG*7QkW4e`1 z{(~usb~bsQX2GI)nuRxAB9r(?Q$)A7Nm$s46C!j#yLV25zM}^3P|zy9o_g+5?{6p- zQHTm>cjsS(A8(#EEiCGvNYMbX^D)=}pOo*?H$Zxe`lnI=iXE0gK%c(10DUF}AeUu` zCuR`P-!2|dGK>Fg3P3J-G0)B*AfBD!b=jU(_Y-HP!bC$n+2G%(PVU&YHDHMD;8o^v12;mNksOE`6t){`TdC->dc!f*pAp}Rc_~>=0AcFTM z_DjuGUGVxuwSup0<(osJduZamzDnQiL1we6axE;z}9aPLJiGTo3IUNaWi&qoX@GP|>%$ z>hR&4F(bJEr(pOK7m)BoLaL{V7(yh;PpSp9drggMqEXLe*k${6R0~rmTRf3t<1!b> zh(tdQ2BVmxtwyD|KFYA1tcYi-8l5tz%s6wCCSM1p3D6u2E;Qp?GMxiZ`z(dfY(oVw{;xxHK% zgztEr+(g%3^#VLA!5=W2#gCnJcC&E{`w2xO{=gDn%-B#t3|?*nLt*bCO5N8ql^J*` z4TT=)Spq`6n=74i)o3F4wxD za-~!y(7u!;l1*R-Z&9xJ^8EE2v?l&cHz&95cW| zHpA5d6jU(1Ek(hD)Q7o(Ri)#2(bd@q#tep0D{LTNM@7TFrIhS zJeiKP@OJk%nh!ZpnCG@qZx8JuSm93uOe#_AUsJ%29g;=ZJS3|(*vA{#I{0!ZK;|}G zh4XR%&gJHpsjKj-6jg{Fr^PDpIIZ5Q5O4YI7WQvb!~T5=*s(LX2%Bec^#=QBw!B)E zb%XD+$d`RD?U=cryxBo98aum-$a!{GPsq(?<9v(ufvM3xI0f3+@n1yC{XQgD6k=?{B1ab%_ zM!rbHd`@t)SeM3mjBVuzHeq^nU2Oa7xYRB1=oBpwJEn{+z+=k%7BEIaMr{FYEY>*H z?lx~L@VL|!ctVN_#7;hA1$gqAUjgGu!V0h-d#S>tt-=YZtMKF$Rfrv@#wzeQHNOhR zm4{Wpo^7U=8cf?7^rx=DNhxY@%)7zIC{^)@HrC*wl494Z!JeNTwQQM5PGJT2om5oi zl=PoRnBZKnU4->u-Ht7?gP*OGCHIo34`hd&%bit`7Rau|-XLGbj8h5(8I*1Z!8Vc| zci}Ljv>C13XTL@K;WR#1^BMEj&RMfn;ki*m!6+jRy*-Kv($mk)<^vlY8)Rr8Qx!CX zbW(G{xx2QHFci=MR8=(Znqtl0`dd@HO8BRDQA$ZMR-7CP&MRYwueBDz*}J!F%wksr zZ2-{I zFR5hJ4o-5_YDy$+psAWy2Pt+5FCR7~}tt?wz#_0>9smC)?%szXaWU78=1o|+6~f^j#jn=)W@s*JP2 zQt?0mDx`@6b9v~G5yL-=2{v9V;gD?1!V9W-OhPwF&4>+Hr(zi+CoiL>F(LVjp+r=v z*yi>Z5xUE1Ry~9fg{g=yp(5YgkBQsAPv-IjRl}eLWm6e*8H+2FY&B}NCZHjAx(6Bi zif!mozsVb{W*ycu9~jhthYqKRtjdK+P69=}HW2K3j#A+x(I(xc!WM+34F#J< z^V4j(3NlsBNaIbQ3(LqW4Q0s$a)r275p4^bBF+_cJ)-Pa(N{zaeRMN5Od_N-sopeW zv3hj%cP$G}lj%oaL+M%XACO*`U*IVN5~_~0eZzbEMH|$=Pifa@!GP)+^)=3_ULk5Z z_1}s9I8&pC_3^*3W$}{}IV05w*TXFn8|g-^*Gd6Hhm&u77YkcVEtIT*d1eaM5<85O ztYw8;I$K#|hf);nEL%L5lST7bPLFHtdEEn}maQg(mYyq^e~Q0BO>L(~4~Bt06$x)G zSQ8cKAtx{~v%&6GmL`trQvQ;8tA6fNT5H%2fQ#8w#6nep#Y;>DSo!Rtr`AB3nzXFx zqamU7M`jWz$Au##C_cy436dGk-xbh&B5`{i5=Fiwc9C4pFDsYpD?-zrht|AiGP8)$ zESF;`8GJ5FDuzC6Az~Z?heIPmJeif{8qj9?@Z73i&0HyA0`p@eKxil{K;OE0P%7kM z8W9*+R&qHtZREWH{DlR-3geUf0S21O!9bykKqea4<@&^$0G_CVz~jOl&d{ z#WsVsnWr)uZDSinI?uh`W<0PMSs@~0!dPM|WmaA-!jUQl$V?JUmGaZfdd5;@VCY>J zvhYm9rliKZEH}zn>K21uM!QtF)rb!=n(_(-n5GJ2Yr!+qZUkyuY*^C$2+&N@$T=#%aYl&7Pt-_lEgr05Hp6n5#)~_3~#9f))8=g$TjrHc| zD=1XKJH-Yuqs9u0mZm)@=L2%G$>vy3II%S7Iz_AvX41)gYg$og9%yeum)yIsg8^zu z*>YuwsM#dTb0(3PU5(U&BUSbO%j)T+7Nz7@)SLs+Oh33l9U}E)p?^g%JRGb}{=iPU zh^@&jbavV5LWF)ikT%0(-1>r1)qtXu1-F~)75=pcJu-1gjwa8H7f#Ljl1E^BA2z0 zS41q0N_?g;o3qfgXU} zZGnBr2bS!gRZ7ufFB?To$zboXz#bi2r}nHr3YIBq{aq$js8Jd-kh37xLj6`PLdggT zcmwa!PZS~;gkaH}Lj29w28#$$5KWUZsh0BdP~nv-bA{>zglDZ?jrvjt!3Icikh--p zbOwf~A|e0+QD4=943ZWjOY0#yg~}L&HL9T?0t6V9dPoF4%7;TZ-m}D`g~Y5U`sj)M zP?^z)8V@v3szE^2ERB^Kb;nc15|p4~#)c}krd0gvU>=;6h?>-< z2USWfyP2hJvW4aPM9o--(GI;53{5r46P*JbOkrq0v3d1%`56qRqS4%D2AZd*=O9#L zdfmvz!LUMC?0N;(0;uxoxek>*XioAVE8|OTs{>adY+l;6ownV1U&Nav&FD2+-cfTh z7FE2WT{pcXd*2#;pz=YS0xBO1v*|^hYi3Nw-&A{3#*^S7$Ln1|27Q*Gm_O>8Q;B4wzFQ8 zy0b1%(OGY>i|jXTXZ=~cvp%>Lo&f5U(mK4QNV~8-ekji>>NP1k_06_Ze{MVV$?;Bo6FPOL z2~5PAi!m#cD6T3L<|7@0$z^;O!7jyf@g#mT4DTrE8Zb9`jceKfqyQz4$gNtTDzS}t zp;)GmSRcey>!xESi|70pqp0l7QW7V8pBsTqSTl`Tp|f^CmNH?+-HWlBiGbyGq1mIF zEv?#4z07*+m|Naa7pK4LVkI_Ox*c{rU7LD5y(YzY`n8NA%D(?$$J0~d&fvARsxZ!A+*gs&oML4-ge{ch#7cRIK++(JhPDFeJ4~g$cEenSa@Bj_9 z#_-&&73{shizea0F^=GOI`~ZNuM{V6kkNdhwyIo#Ll{bJz(-F!Wz=s>s(cOM!fNrY zAuM-CwKyY3Z8L&U(NoSsmC6B(ZTS>VvchofiQ4#TgLa5BtxX3F<0Ti==4ZyilkuKy zF7eF6)=>8foO`Ovu}opR7+eBvG|V8%d(pLzh7#*W8lPoiy8aShKO1^ZZ`{4ki{PyE zU5V(|9wsjUqfP0R3aH&qV3~nU+f>g5A}WvT`Krgkm~E>u)mF_@o5d&jN^s18;V9(2 zwwJ;qQNH4NG1Y&mE^fJurRDoB8wxHZq0742V;j!0%W{_2rRFTxr{FBt*p=!Jmb07~ z=Pb8kS8Y}*Io=p2*-U8th#PozGlXWlWJZ>3|$88 zlH!EM%Y3nDlZdlSGoAFm{o9I?uA?$l&u*N@j4DplvKw}8%PKgf%Csalpr>zFTj)o- zN^|dp24B9?c>iBuC*6&yC*508OuDP>qePr8c0!H4q{oYQ4cwwwVi#`cCHW?KjVX~2yTbBccl!)6us@$ zVp_&X#quhS;P4j+#H}A*_a~{rrab*lQaT3x2Q^IN^YWL}0q??E>WBQpA3?41*g!N@ z&_IW7Oa?$(d_8GPPc->zphE#ZQZf3V7V02~hVp8r9~@qU1lsr~>cvUSDSm%qFrr#X zy{P17-C_{}DHTqd-X@Y&os#KCr_;rKG+#SVYo5(&y4Y0|=ZwkW^)261U6$$B{tC5> z{$OCal)rpM%MZo$y>>-cbM2&+UYGAKV;reC9KU}1^mco%oq9K?o_e>Wn0mw48A=38yTF!ens5Oepk68pH zu{DUUxL)ZK+08RFWP?C_b0X^85_WPB6~##5DRd!XAT*{=gbYsNo5hZc&nB^_5ww1}i?CN11kRbL=`A;ur4CZt86Dv6}as=af$a-#h>fdF<#QrZ0oLZpLl`du}vkp*n@L4k_q! z;guGY=1{_-{G~cc7z84Fg8Vd$FtTz(wl55^5F#q}!23py~#)Fuabi%)>q z>p_VsAvK9jXrdB~xsqz@K9p0ySMJkF3IuQ>kKKd>UMn8&l#{+7tvT{&6YExys@S&X zb%3Ob^6}ZY5v;*=5pr&JvFjpE1Xv-#b0im?T~+F$fiugVS+sj^vKGq&8`N8fq36;P znd;t>)pVs;BeJUTp{BT(gL)#)6iOVAT)R?J%a{HK5sUE{wCwW#*ovj7WjV0M#^*EE zw`0JwVlwJ0`9l%yV_vPc0$D58W!AQ!o1|e&45CH&H>4hlLfTUU8y;wn#6=@OqCuNN z^S`-wRbWJ-b$|7W)OLT}D_)dEq%Yg`Wcn8M4bJhE_DZwrOJ?*0Pu&~sYVz@f)g;+< z>k}!M_sv$@vD-55RdMFM;z2IZ0$Dp?W*nrp$;8voYyTQy-*zz!E+~vkUJo$v(a3%I z3I_aHIKSY?^2V%0@j+@2#;cV)Waq}XWR``kZFCTC8aZ>nU87q@ z{5zxDcLXCrX7%7&JLxv>+`dh%BEeo*Ao8_+6!5l;jBMH&oV$I?2+G^KY4fQ14I?*P z!&FG?fP_jSCKEHDBo=iw7cV$x)6PvnW_TcY3jTlQKyU`G_;m*Uzje!bn+Ad{BLl(6 z_EG-3Wmhn=d+SyuCF(gU_wbnagY}z6FW9sRUtsGv5NzKGqDn&n;S_rxvnB;-o{P4HmFK&-@F;&fI&Ifx&4A&!RAe)8_o$v zHtiV=X4IYSBU>+IIUBa`9vRK7L{#do-PuLcYZJ`y>f*Sy=ZNSG zm5M8G#eor@G<-nR$UsCShK?-!*s%=+8%EI^=d3$_%gEUS!S0bQ+eZfac5XTQoY7UA z(I=}m?A*R<7rPy=uG@uoH*8r?%~@#4NN5Cb9kAGxYSX zO*=GfEd9cpMqwF{N7Y3+}JRzYpld_bGZ zG|_eAreX#|!ff;SYD7A4;=xt~lFRflk2@{=3Q=|^4MQsWSF0AA<6tluP`C|$k>ts= zGCSRXys)<`lC)*1t!m5fV6!S@dFDVZd%iuofPv~N${exHP>CEYxxvZIB(j-DPjuSp zuFuzCGojFE_)St`Xq2Dfl#$8Yulq*W&YIQABuRdw#W~DpJwK`U=69G!04@zsfy?!x)oG@0i|Z*SNn;y~cem#Ts|9-Jp!wHEvCO zjk^;Y6s0nRObek}((7_+W%HdWJx9XfjiMg&d?Zae-rzGuGAR9DK_iFZsR%F25O;#T*jynFyEX5Zj%&4`Ka*tT#q zDHJA2k_gw>CtB%I>v-KQ6T9m#6=pMM$SkIFWk;E)A(VRBgw*JAq!yw^9Fkw8)x3nw zY%=tv*F?FTuqdfj9ZK(U9HoGsin#@=7O`oA_-EEkrc8goVPa?S9u%gt!~JVl_n&Eb zn@%0A`_F8o-SZ$g!;;r8ksAlABJY%t6kt$ytvnxA78h7%n8aW`+ zj@AUW33~r3^T9*Fl1x0bZ_|fREu_l9%j!k#@MM##(+`+>Bo;JjOl(N|Fwf$3Hg#sM zk>W87AmWP?jiRGWEh<~P>UT}b414vOw2dyR`-t`Ep6Vf-pl-Ff#jZAAO1;{AImK$T z->x=AyV^V>zS>*?RjgTUke(0b2Doj2k5ayZ@FwiDp#mhdpiHjngkYw?5PO$si;N4(+2km#ZM#{dL_%eYONn;!CvFBNNJv&fMVWV9t!oR(A`9qPm&%hFRu z-hkjP&SoTpoPxpm#cCbNi}WTy*~RjuxjfP}>I`_3ed9QdTS9Uki4nuFEd^2&2pQAf49mJ)q+9=6E zE|FJ&qfv)uu&f`LcO~D9D?zEtYT~N`6`(C2AIp9U0^20+tHf9E8OT9Agr(ASg;}}c z&0tm+ac<9fo@Qoeq4#FS$%Lj{%ur>W($Zwq=OULV0_q{vzct%uJSKH-Fq5s@}^};yrds{7dR7@sku&;tD$@USg-j zsqras4f4gnJzA@p=*n>tb`kWBK;I>}aj6`>Zq-#wHWo7ka)@Jqv?pUVDvkOy^qJEb z3Uo!zDBlm2*VFeA>(f4B^RirW5tp(=ZKhXoiqMDiSlD;uYuIgVz#`&-v)^;D@w6d` z4T1wR*H%W{BWw)8IP_Vv49%7rHSVkGa$J{eVC-H-1cx40Tdg{PC#aoEdXk$d#C=e+ zZc0`Mj88V5tfKlfu0eoCKa80+!WsU%2%C%OFj;7^S@g4n8`Yvkfqmk9hTQ1 z7(6qO`+jezWtVk6k*>rjTiXqJvw*Ef8F|_=N{ztNoD6Uz?`|qhJUy0cX1P2avoxbN zqL`-~k#N!cbZ@`iknKzLoo<%BP0g~;W5-w_yO2Pd)3@AZx#`bSbJJg>;HEFP-1KV8 zO`jF#rk`?K7~xsX{8PtiP*`xRiRMDcyjpS>Cn#uhpaKH_G0eF}i+W`A8)4Jwq>~t> z*3^?H$aCZUHjMd1kzceqj?oDg#U!A3Z{AzfDvc`a>f|DBi(!V3$MpPiBAm}iK9x39 zG=GUvG3&oa1=+INbJx*!-H>r?RG4)vi@V{|i3%Z@!jgJ-ie7BOA*88QHPE&(9S^<~ zO|pN$qR(+0!+~YqR9ekracx&=xR}JgNeMG%UMJ%a98@&O?m$t~wiYEKnQlupdlx(F zQR#GSLL{`c9!HQ`v=Gox+d5jZ%IbgsUBfTEjFmTx%P{3jt9zFlh9#U>7_ofMZ`BrQ zAXq-KYTfb`-R**&ZdCIA|=AO6!|6`bA_?-Y-!xs*eoLu`4@> zVUU7=h+`NO2I_8nUnJO9rjedn`3~#kxnjI(%!=I+V|%^?`*>>RrKP*^f=H!W1+Yq$ zg+kQE$M4Ku`Ie?5EI>CT4f|-giXr2MNFotRqdaIrjcF2cbEx!k4wBl$x+5De&z0`bz+>Qan>%ya{Z=KC-6{hOUUg(2;kCt-Qi*|AMB z<;W7H_~AG*&`P#sRR=h4i-kAF6Hg4bz-bF^6e^@x<@=T`0O%YjJK+h<$rjoVdR6S$SI2zZL(r294 zoracczUL;OIsoU@V8*GT(b-O^JIxllL7j^U!Cv_en)zkQZfRFaE~sgMlLxj>-`&#V zPm#bNjiJTq4Yk5pqgsMC7N?B}-^rJ(VMB)tsHQb0LmnkD13TViL62UpuS**OYaPYx zxHqdY{`I{Bt!sGObm8}oW1zL;c(R5P78HXuF738GMKjOFWUX3xL-cp&1mX5)C{T8l zQf_=piF*!Bd;KgA=(tk^hp5ZEnzRciKZp6~-Rz>y#7$vg(RPppyEdNJX*c#$90!XS zsNsMc9yA>nd)c*_@H6-@e6PI!rQLAvTiI%AIH>x7_}aC~fB(`S z;@i(jDg5<${8~6ox$FcRcJ9Wi>FnpiR6A5sxr%2jv|j-@p@o?eelcW#zew_+h*;9X zsJk>d{&_lH%ZCCZ9FQ;K1)eT$g;Lkh`Q)#X7g^x5r9g3X+=sUL9SX^egm+$mdvDnT zHe0{;Mc10UACS9mjo-aT?!FCoLrMPUzyE%Da+5qc71aDEe3{bTY+?e~dj6Onmu7MElm0!^@ zcQJ11F*hbp{~l$YcYOF|`Dc5o5jbv(pO9ZkBd~x6krDV_>4E+6yBuc+q>vIW{y5`^ zt;mzv*c0|;O`gT>zf_)>ksj%Pv@%;Eqgr7|4TTrT`z?n;BfJ63 z#CC%l39Ko*;c_W}BjH&+jRdh#b{GcXn}wsb9F=TeV)=86rMFQDr_d%i;gwRT3TSd8 zvjZe{EFoQGA)VDz^N1kOsKLe=Yo&0Z2Ur|cg!OU@tGAYds}qh6#EK)FrRDtX&`KQ2 zgmaCBb9!&BMD1GyP57!F8i-Sfh+b_Gt?LO9?8Z0~2XXq4J)^j64%!hOKdm@jhn5;V zfH6p1*I8U=_Jm7Z12Cp$W+R|XfLDhB?pA7d?BVqWP`J*m)P@^#-FcI|u}l_`yV|Wg zZ5qPSy-tc@L%hY(-Oz&WO_uK6-O(+C`#|PML$jRlE@3|(l*@eUt1!Yhb_&2vNX4~p!w}3qWMBg^ZS`w zV7O_CD)}o*bBm=}Y(aDD64CrmOLK>%d7uT&U5ldWr11v`7v~P*0z-3QOHW&=u`vm1 zl`!}AvMI(yH-f2ydbEYQ&q7__BUEfgCko>@y**ie?h>P0wjO0MP8f{gOp7j@TB0r# z=AybVG&C1_Q$E4O2OUOq!=p7C-E^62j=(m@tbrC*S~N$km*$A?P5)80Hs6-Z59IP= zx%``4ekqsV$mKufa@3D-d5~O=mCK{$@&vg&SuQ8ZWrbXxA(zwSGAx(%ayeTrTjlZ` zxtuSTy>iLnq5*{ulAZaFWuqP2Xu0t`Yd7!wMG@z6X`JQ`2y61L>H}wbaP92mvpIQ@ zMb*4^)`kM{I}2Y0tt{2Fc|%Bdu}DziwW70asQ`^v#%VxVH2KI241%t9^|UwbRPj;T z`6xjp6TV0aPgKjh#<*PGA(Sf&l(6A7hHhTJ*01fV){fLwmeflvsk!!~Uf(0BYb>c( zT2k}vNxiX0QcjF{oh5Z?dr~*{Na_Y#s~at;%REwQ;O;*od@BgF9<^`lk^I{&`CBac z7kK2op%T7R-ryX$xkqa5WY#+@wHLLZ_8v>^uhLM9u$?0lK-!Y++;oST9{fdH#hNab zQDr(!S|-9;S=~pg?;K4$d4e@D<;4d5k zYh@>dEe3+IvNOrwx#n)%!bo;;UXQ?@d%FSl_ZHX@2h1QKt`UPEaZ%siBZBo7!8a^| zm$pE#vn_&MJtA;x`S&e?mw5<02Ces+;rTt1a%=gIEvc*9le(}+Qfq|(huqICscRgm z=#aa}G_3CM{X4>aLid;WRIKNbF&F-a6v$zBagX3OT5!L!;9ljx`9fp(2TLvABefNI z(-cR&8^0*IzPbgq2gn<&+hmW_J}7QAZ1{&*YOifU?PyDFx<_iqNC~8NtflrkN6mCI zmZT6ABCZn$gk&jtHynjbFftN(?(KxFyUEBM2OLVHOjywwU3$5=ewxG%WY z&>nM1Kpq(f66vl-$>sy&*H*ZwInU~cFAb_szXF#aObxkW~E!p{30@-9n%EUmoT9(@0%G2-Tl z+laqQzGKO6^yY~cXXd5rLO4$+ZfQtujj}19tQg(_<7l1eqUH=JR?ZDyb1O)&j_)%_ z7P=X&@kY*$DevM)Z;YsxIoV)!enE*wow?5&OdqhAy3yb@VNx#i*T&q@z)(Mg@*|9r zqw*u>ZgkE3xPW1!pQ@&&@IfVh_owIJ?yR{Rt?M_01nYWG)%ESF zt_M>NFbDf%H=!gBux}gK3ybQ^NhFS{!}%@hTZkt99?Fk&Cae8Jb2nPSAKMDvsw#M^ zs^CxLey7g-H&bS$fqxObo3sv3Nb~(#NSuqcRjtGS2Yha9Ea`eE{*Q(GU4 zm%LtW3EL;ZcZ#^2E588f-jzYPCFBqrWMHr{GuHvGCL{;C;@fQ{_uCfj1{Ifx4O4 zh90}K5xv{8H4n)^eYVDBN?`rx0R6k#yoF99_q4NTHa$)QV zht3P-$wjdzY`Pc8ld;$n4xJavlkwOS4xP*8N#fACLY_?PZ=%h80C!b$KjD6=xpTU? zUm_oM)!Y>+iz9AUezCc4N^I^cQFfZAE9Hx7)rshf7ugpt zasf6MRT)55%g$og^HtCdza|$62ikY-!m;rM4N}wB1>9f+LdTLD;Wwnz8^q=GpYUrW z{HFZWjP&7k-STujJXa)h9qLcHDzc@wF zZg0VGAQsJVAXPM@Ef=1>1xJ}8x({cDB1Ez@g;Y7a3I&{RT$;^D-jj^nT(N>_9uS<` zK;}@qSe(XYw1GsyZF6cI;rHx5ONpha;L-67VQd~$Ttm+}0ayrJ;#dP>@^(La6O)xP-p_9u~yO%M22x6NT z4dKpj(uqLDk+#A%UYVR9L(;fHb++E>9b|&7&X47%pc3$2LHoJJ(D4P{$;eMz2FmgY#$;AF8ALv3i z^A58u7I#BHAGo&w(dE4Zpjhw?0o~m_AV12ceJjxgzXPaP_zgjQxCc;Pj7@t`_ekL# zK*fS@2 zp3*J(Lcp7m268A6cB@}vHk@Kva)Zc0gsLQ+b`6Q`kPjf1AV>)0A`5FXTNK5kURaGC z%SH!w249lz{2Cl9t1b_9MdEciN5YSXZ-&o!_%BN#x+}~yNrp*3vks#& z7VSfr--yF}lm}Bq^rQj$%>NAgewboid(|7&Xabf@e9KNjai_>6P%;pa?0 z=)C?&FoF|NRF*kYGd;@COH;CC!SgXW8QmRA66}<`Yr!bCHVma#XClnuUe8VOi_}d) zr_v5h5evU!Q!xC>ZwkLB&s=0r{yH^$y2EyWAB*H7d`5El@Smu=3;O3JV+_AbT@5<) zcBn=yIE>X`aF}0>U|9TF*SO6B6mO^3o|2nhM7gsn>hn+aOUEe z!2`sbITcf;3v~zdv4AzAXTVw#`u)&5^(t`aCFP;1(bM<41NvAX8qqTl%}4Lg-!m3D ze~(KIpI+-7;KyRx2%j-+K71qX$wG3*;)c%SQlqE4eFyZh=s2QhbexagZ~Uh$vhhzy z4WEGm9pJ|T?FgTNc0T-bHt>)ECPb`qyAkA($9K*g3=~t~rBmHC3%D&Mu>RClW>`dr zD#zmdSY^ifc~$P9B5H|rd1|DL-ROWc79dEZ3=s5?E-c}aFpwHOgHAf2j|Kh_Jp=!I z^iOj#JMuNgmga$gGrf1w{h6sN!~mKO6^ca_vO3FjJ|{Ln)5)ljrNADd=&}bXMx>FkY!cbz+f@tWHr3Q+{PmQign|#F38aU6R>RIa8Mp&tq*!Z24*?- z8Tqu;X?@ajIJgVhkL2A9W(ba9#S8Mk@}bHBq^U&mNgQ5{!)wOpgZ%hJcHR07+c3kj z5(KF}Lr!doE|jmg;JnZMl}2e&ANm%IRvWNz@T{BwX=-sQr=6&UNmy*QOa;Zdez}IT z@BGI+NgHQ~VlPyJ0&2Qa#UTJQf(=Rz`FJ~W^HpU7gppS7GgZ{tc;laCB$cKl^TR0& zIF3i^jg|rS$=>UOrvH>q?xjOy!vDi(`M zR6Bb>HJX4bS&_pq!w#ro(WgYk=u@w)7Eaq_6l2-^JE|e<9HWSIifkBiVsu&A)Wfd>#q2Vu1-S86C*-3*Ii*qF^#<}_pKjIo* z4(<@ksER?()W_NQazF-cwqx$u*cdxv+!qh|POsfD(cB$6QSTNWRc(}}Jm63rk8;lF z=nilr*EwK`N^`DX0b9di zS8_xqjwQj_0vvld0$c0XL2wp1p0OcU>9=jckzCg1G+>t`Zb*?*mT9Dko-F9o2u@kO zdLTGu^(g}Z&b$V%Q^iIs*rsS$VsYRj&p-fs^bOiL^h}tonkUsNSUK1yx>e;l3o@si z{46Q|8FD!V2Z({!&fv(mS_9dRv4~*#fWTj!H`Jqr8}jNXF;g{_lFu1}qxFL^9}B{~ zJ8|KZ!-sLi%s_zi-PZD_tEwr<(82nc!=dkDeK)l=rf~LD%m(k$zYdB<4rlO9=)Phr zNBS_{dr9TCfzisquMaC2oG#!TI8=QS9c*0z0^~!Nl*Ka34v4Wv58xUOsm5}Tqf^R~ zx0a`}nhx+jr-MK{O!R(9bSG$rW#$eb70V%xjs#RDtH-K4&Z8jqV`jkl#sX8mSF{mNNhevW#`wq5p-4D2YZJNaSsC*NEVPXKY}9 z6nJJ6vX`-kaA1Hw<`kj`dJCOp+96U*8*<5R7F@gFcwzG5n_(Ep>zZ_ax?HK%ixal# zqeZo$$*CK6+X&P-f8Kh9Zj0a3)L^tBNU#c8*tbaPAISG_xys zt9wKQJrc$%?CCTX9<80=h5|Via<6iFLtF>F_H6!zu-v>*hsmBYvnlEj%g?|%9F*_U z*TK2&s5A`5LYmyL)fAvybmtYeVVS8RJt(hX(FJTmo6I3>Bs5_%+PM^HU4ST)J+KLx z>|s%8Z9a(blGJFQmjZ1pUj)%IU&Nx&+LRLE3sR$fQ3|xqDS;^`h?XfQ7KPSkr3kM` zjrPw|pmjm(H`;N_tQ9?>U3eUh>?j-_3U-&lv-g6-!~3xW-$%m}Y<7$ArKzj&vJ};b zWxrsfGy8>CjfG*XP;wHgNcudK1o8spq{#u%T;y^hE<%!kc~)M)3fm~vMG7v=%+?{; z`WmjbT+vKkn_DA%Me4e~GDTfuxi?r>=HBq@dPvt*{CYH#4)QpP8Ha_GC-Fn1qs>+L zJJ2;q9ry*!O{H3=?U_g0=vUDM*hNFi!W!=|touf?DM7+lH#d^r z&Ak$Ie<+OHO&oYq!CM(9&bp`^kgoXNC*DA)36lNs!* zAXq@Mgd?jyK_ttJK0Hkd=f>$nG;n_Q6nsYl`9ST5Iu-4F$IY=0S%uYwe`e)~-W(vW zhonE3uTBm^lQj!(4(v3CFhpe+z=Ofm=)`Ch7p`I%+gaIL&sC}ixF>)x9!%`dm#5ji zYQLvP!BDV4o%xRTFav|)n~W450dJ%#`vH$~jy3N--PLWyqG{>URASL*PB%?lUb!iZ`Svnq|?|o#|Zo=Nnzo^hfU?7Kum^&);Iiiey>mO`U-uVvwkq;)SSg z=mFK8cK{mGm@uJBdsH`eMitNbLSS$19+;ovr9G&(qyY6nsU=&BSzrk2t=)r)Cw(!% zl7+3er2yuZSmuQxFy@8v_;rWD<%xz14?&zgqZG85z1d_Y2yYQE55O{x)wVtCccg$F zOD03uOeWJC?Bfk=ZLAv#;O(!gaGtHgyHi);Jt?XXOFP3V9EsV37SZzCE$k1ZhJAMm z*s)wSgw0$vy|w(&Yk#$A1lT@cT&7un#ci{v#opouP(9z%>o3|(Ya_ScN zN{SYU<@I3;Ft3l_0>;ie2}w5qT1bLji|{rTQ26!K75GMq3dB+cu>wpL4StxS2FFN(9tsT1 zX~Y^Zr;%5KJwF5MGhR*hgheH`=ah9PA5`;m!FJgL1!Cnfs#!?!yPyo7tp)b@*_9C3 z^JUC9`e$=cItvloNbD+5CX0EWX6`=wEi|6fR?m%jYv-)7aOhK^N4ZO?xpSN!aAcT2xd!pn3AO5u&JDdM~Kmb zu&X#aGq9kS8k5*;nv;!Vt`%)t%&dzxl~(wx=KAU_|4M|I@2XcTI4XI5P=b9YJ$sHG z0AlCK@-(I*zJyL_Z$DO({(Ul+dDJU*&Eo1`h8-E(^TJ_l;qf|POnSTT;w0vk zKk7G$R!I+5vkvQ-4-9I+V>@1y)LN73_)Su;4Fu*?PK0P#3yYK=9pGx$--;{H#RfVZ zt?QyC%}nVwoI*1QRS}+*ty&QpB{a&Qk~U`ZIPupMroJ%HB5Xle+EB1*G(XLjt02T_ zMjCHIv>J%#t`^D@a58hPBCLo_5$B4!9#QtI?BI%sp^t8cW=Q;$U>c4e$gIbeJ*Qs% zT~jB=qpzWGs`v9CWcdYlCZXLWjFn+`RH>Z$>L00!6t64XNw6pS<4lbn*2n+Cmc`Fb z^hUTIZa4xrGSbc2$Zb_Iua~KoN)_`TDOgJ^dmUNJZ{$10T3V@MexIV~SZX>J{hzMr zw5k|C`JCAcC#zxpkOI`LU`I++V17D+`mdfrF%g{xrTN|aK>#T#z+fPUi<(xgbvX7N z)@oRNO@cUmcD3*q$Q^1L!q#1sZ%iUSnTcR~NP#g)Wni!rib(||*9R@5!BmE;T^0II zT7qSffKMS0WCmZL1wMjEII#9Gfx7|*{7fF+9U=jMRTx3Uej&*OBcZA@(E760ek}v> z11^p?;Nr#OZndLVv8vnwP_WD)tRwQJlAxhCE(G~Ty@L2F?W1$B0Ut5=iizk|o5;&r zqxp%Fcy{mHn#ED=@JA7~b6GLt3hPp?ln235V#OEBTqRZOiz=2BEJuu0Qdpvs6--O0 zzKt{i7UYEb>T1zHgk}pa*R%QgQY8;ny)?ji*!(2ayHuGgR3~tpHe4-tNQ@A8ZvdGK z`ft9C!B{CBLTjMUu}ZS~!lNr?mZ}LWX${Um^$ED5Ok=3Z8(@sIolJ?6-Yf;CPO%!t z*|6*rARoD$jv3OEnnapz_|ksOl$EDKKaQ7Qw_(HJ>eXw&rTgJIfEv`+j3Mz03>UH- z%Nx%+ZS~rd`!91x&?{6ZOoouWu>mUeO05!Xtxrlhb+GtZX+;9sV%|Gm7P+Br{s}&zAY7<&fb~@{LM!XgFlDGvoc_#SYGl zu{@Lnb&FG>wDMVaP>Q0R&jJ%svS=ov^yJtsw0O@{=~IS+NG2ZIunxgXsE}39-3^r; zNDJ#Wdu4dEK#yr#lUJCBr>JBs$0aNI2>C93C7n~lNC)-rF)0AW5?m6{u`xhuOs`OfLsFl|27EwMMP9n(kEB* z0XWNJY!TDq^DegqH+v~QaUP9Eq(u2xNa5FrP^qzP^P})h9hI#w+#RiauM73%ShG|p z345Ip8FX9O>S8B{Wh*ev5P!@(55ikF!B^fj(mXgic1;tNQey_(+|`9UZN>WyUsnKx zppc@qQ2yBGC7rP7*kyXjlr_tCy~0KPqysB&lliMJ)DCOBQ97VT2ZjnT=$$-XvE=ca z)RkS^MpVA;rKDQVu%W`ij-%KT7DX;2(4zoXj_vF zR<7IyajR5Vxe~z+U>*$;@rPNzbVp>kjTGiE)N;ALljZyMRY-`C|LU!a*jt=jgV>wN zlcB@9v|qWDLke2MLPQ>$h1O1d4yKw&Z+ zy}c9rL3?8V$@>JcN30->N_jUpLb3zG8Ucw9&h?Wbh+=8T2rq)NssX`voSHmVX%su&6bU}qL!$95#^V{>63Xas-f0+ zVYTt<5Dj$MxtuOrb#yRU0TlUwy9$mj=!ccFxt;~BIJBA+*rUpnGu$!8k(;NAW>{3| zT-s9N2OG9?rdtZlGYKLMcO;lheSKoA4lW z0QECNvz?BY+sSk7Fm!|p8|1^FXtICktCrIpS zkkRIA<-?cDd7Y-39^Y~Sd1XGvNzWXGfbvFRv^AEwe* zH$gQbzY3lLqt$ubMx%Jp(X!-G9V5Lq$X>%>INjwDqT1U@%%)_FN>3@?6DM!Qj?D-) zYhYAhj`3wR&U_KZ@nzw?BKNCz3}Z*7!Lys?-sX}lXB2Vf84zOA3#P|6u{kuD7DjC< z{_teSo)%7T`oeNU+5(&USo#*VkO6sZXp-%>mr&14!Q`B$2($8&$vr#H}f zpuNLDjBINph-%A5uF+F`9^ZT@?XosfaWAHV%f-dEG8rKMO;DeMzBn$uu3VW&&xnOE z>}J@2p3#)erj0GxY*r*fteLPIi&0K=0~iLfO&87xeV+*#!YQx~=6QoCig{BlLLT+_ zZJ*3ukPF13f-lX45CJEb4e0g@v0L1vVc>&w1P|PzD4-FC2y-CF7OLa(Dk!mDnN>d^ zsi0W(Tl{IqY6!x|PF{(jBGrtGS+Q~m%5tbi`%G)ZTQ{LOU`BRrFaTjsuneO;1_IM4 z+h-7;6>SvM9g-Y2+cVNQ#-tdiK8?0OpN>DoYQd#xzE~(htxsW4wjdO!Mb6aAhcr|T zoYOI}brIG26kLrfbDjr88OFS=#qt64mF)f{6t7Sntf46sGK9-5VvWHnM+V!oD#eba zAzCJk-bUBf8$_D7SJ2w99VpRBH?j;t_9X%oskKrKE$;IOiZg89qhv;1gbkY@S=tx9 z7+G191eR9Jlq%C=#c<{iZF^9Pe|UO}_D0!8nhhVG0l)+eYjQ$G<*-I4`C+tb$a#2_ zi6oah$xICKPN;g?a{l&vRhG_9)!15>107IMpe1h?`kG?PDEoM|9X z9iB-TOO2Uv$eMPRgNu`UB6LQ9Fn!V7#&IAdV;UDWBqwI*@vWGp2gS5)Ie(})t=yzs zLRtZ$zEDb3+tk{eD8UdS3{RQ{hH(qw!iZt77Y`LqR~(poN`9%i4+Ows83RU56OOOa zd^gQ)D$%mF?K0q16x?D^?qkip1WE1DIN*l3Knbm@wWC^?l051a(XVFFNAkK_Ou9km z5pUNM)YxeSsmLL+fWd?&y!#|R8-|CBsKU_)n#q9Cal{a>W_Enl*iV%<1}2p*#;(R; zphi*#Xu4z0hYtD`#ev){qbK761ED>!HFZgY?lVzSsHkP9pkE0V<*g~Cl-*F7nQ7o; zg8A&mVhv7H^V##)WH&RRCzgWq^Q8tA_mrDVP?=(a=_hbvMNN}L)(N*CuzDu*UZ)r* z)nhsai5fPwdx%O8*n}Ekg;mN!U}BB}tPB(nWWULg$%fV{H!zRhU?}Y}cQITyrS!OD zAYs3KV3e;^u+NL%@YNK># z96K)jPMG>Un%0V+vVxnwfGHf#aUwYB@$HdowHZs4y3JNf&)+8Vg-GKhAvPkXaxyyp z1TUzl&3qp}_KgOLl3E}8u@mc4xZ=GW-(47>A~%w8LRg4xAX{Ly@YQlZOhq=;Zz7MZ z?ssO7s5@7zBJMTIQis?6f9!pGfLukfHxL9}P*D^C1rH0F4J6AOVIjb>39yiCLLLxI z;_hU3HajP?Gt10w!h$FWR{hD;QhY)nLkcu`}FDlb#--hb#-<1KH6;7Wh)_knwW$TS?xQ}FxA(qiREbE zcd8dZ`rZ;*A=sH_~ZtzOU zd7S`7QFC0K1ZRis_hyEb`%qFomrikN@e0v1i(YGMvrMX_m1>+uY?O=7>aWY!WLTF6 zH>B~poNq{TjgM!)pw7A++a1_Xz!Di}njt35WMjNZweL;aG2N7jCaOUd@*LyGh4NL4ZE1;cd^^E6KvGMv5_b;(euC4q5zOEYPfLI zD;?(pGf`n)HH%Lr2rMVb_Bl-+Sgv@h4o4+2%RBQuPbthrM0Ix|6>_vxut^l(zJ&HX zX;*aUrwt_~cAh0FAphY{1&BmaQCJYPUbFf$0Nl9;vca8CgNJ zZm;$<|L@Y(R0~rZ;PMJdHQl9Rl4VDA#af>J7Ys?`vS3PUO6Ra^sL*jvkHW+hZY*Do z=5d+A64RjC`Q;5Oy-$RhOz|)|KPQ)8cCJhzr>L!vt1w$GJl$~7l z>W}QGDwp!xPGIA`kkikQ#;W;Pf2_V4o+uWv8f>4{jGg#B?zE{Out1{G;g~P2Oi516 z@O6jruzsEtiaH3LqRuZ&Nz^L&FXv}%S~EV$&7XEDCy(w)WDZ*69=gnkE^ zqec$o4H+k4IcsuAu!(jFAB@2ERMx3ZD}u^s+8K45& zrZ$vM5AE~2nPzNTMwyA!LRls2wFg94);6dQtDQ%))p@>H`D98k6%q%c+%TUNm+EAS znIZ)r=E_W_O(mbr_cf>3gwiQaf)elEha%F*bJ;+8HP zH?`W~P3{IwYSm_R(V(ysx9s6U@QCNUnljku67~raHAKSlbY)_Tx`r!Q69=W(@u7zM zG%g{6U7Vz-@`@xQAOa~Rv=WS*G>t?iW+hMNQb8Sd#oj(Enmaert8OSE8FdvsVD(8Y zHp@Gj3=O-v3M5@?NeHN*UK3t}I}uKwj5F)RW4!beuvVkHQ+j(wJ_C&C%#5 zxus8?VVILSfxXM=WDgwz!_FXCKG%|N#aqj5r98O_P~<8TTUO0BN2+}fyJL)91X{C> zkmCQqnH$MTkk6dn+WV>}bvQI?@fcJntb7E>(IgL_j3rU|Z$*{u#Y1$8N zs%BB0tlAWAbktXegk9(EXOyB@uF6s#BnJ<26{iVcyv!jlhOtT2@o%joT&Jbkc!au+ z)n=1!RIt|Z-V@20d2_^cy(F{N@#zd&$JH~8z8U^dUZ)*+;P}&!#!reV~<)HoK6Y1WpLjm?q_V)On$grbB^P+`X*l*tQAY zr@$749395-c;leJN4slPCk882M@~btsWg4oWne(HZlOIP2%lTp6oqy}O)%=tsIsb) z$Dj7#&TipSZRu1Mk)RnJXd_WZ9()|9g-)@-NpV2!l9I!lv=2w;;kN*z6{V}C2NEcq zTo~8T$#p(7g4iKu)!c%TgefkQpB=4yO*P!3T`=3uQL%a;&LEw$iq909U=7HjX-VrN zb&}lN;gi@TS!4+hmeZRWmhcov9)-H+83zVndnPYbGZXOMj_l-zCk_oOYr8eBrEy8X z6CPQ&bEV0MJn-0KwNT}SaA_0p4b?`B!^NQ;P17d*ETp239h7>Ft45p|Ak4P|GO^({ zl={m@V?MCdS5|~zr(ZFG>nd&!Hrh|^90@M*Mo7Kj0ZwoLe5V>#J3s(D`Y6s9Mm{10 zEY_hd%;{dIH01>WmxsEDwKGt#^+z2A7daG%(eM^0KNQ zss&C5*-z^XxVx}g;b8NkvS8MqeL5#tX?2vY+0i_b!uAll98CP6=+|FJ>eo7|(>+^I z&|F#=RmaHrhJ75=SesL%b122AXUXz;HnbBhE8Ds`pd4BLl?lur67b4dtj>)PiTS48 z+4{t&IbKFCyXr(sn?#I)iH#Jbp#q&Z!P#eQg>ak`J?>Gh-iTDybw%I}vf{xTxuIgF zAxfz|eBBdyJmR(hHpHpjI#YH{lVf^8iM6rHM!N|?jT97(jw?=7iX7U5*B7}WO|%$| zyGJUZLa3|o%a(&SM2HV`hE#D39V0R!xwv4})yaJiI4PPbn2rXZemU0TI7KSpI&^?n zu%?urHHl@(pps7(S@#^XDqT+Jsj?V$P2z~k^6vDr5rC(ErF5^I~#&gOU=UJMmO!yrFu4=%= zZ+|CM!v~l77iyjA&J4J7LAktcVyxT5OAM^zz`onTS` znc@raU?dkHBO_TjZ6uv4)=WPXPKASFJK4}i@w-viZwTIb@va2r+zFwPMRxTvgY3P6 ztZ(|r=yo^w``3i_{tVD|!L-3d`+z{(1VnRGG>>TUhfoXO{cvFhbRR(;M)y%cw|M%5 z!^RH6O7!!*=L5V*0De3Jz>5XoCk5b1(+6m8nd`TwjeMM!2+m7u;ye_5$793B-u;Zh z8GjyDGK9LJ@ddGHhS_Dv!8W^HSJ5xn?C8}D`tLjwU(DtAe7u@pZAH)c3-~q|2Ub>G zfw$b9{EJ8EG1;j_+ilCr3&|+4Bu_Wp;Jn00VXUsi_%bTYA~CK)M#i&r6X9tjH@=1% zT{@=7jgJi)dR-&M~{A1M0lgVuv&{|o2`=&>mL1N!2Xz!|tHcevRZP3P-*O1sh zMGltOyEQGbe}QjnO6*_aEe{(0#S-$^?7RqJ-K5BV05xV2*$*Ql16sDJ05y`?zeSY| zWcH^;8a^rjn;5qMN@!ErsoB#9dT1=X^kc`5^3D`=j69^ht>G=CSk&(wsQCTX5CaN{r5kTm~9 z4wf`~G%abin4)iMN}8)A02cq^fqi)PKJHQTT8}@iX|>i}L|%h>YRi{v1;)4Vi($;4 z9*j+JZ40_sN@K>b)i-6o)#SYxE|5nYd|%#S{?ZWmm4WWaTo;QEQ+nl7=bBV-lz zLER&u?#%$|X9DWy0_x~#_TU69%M!5e6Ij2@0Lu!{`vq1LlYD9)W)PbX2*?L(f=m;e z4;zs2l{LiXBgnyG^Od-Hw7 zptJUoj|sZzk&Xh~)HI329~W?+$NKmoiH%Y6n%HHZr3_3##ZuO;MD)Z)N!c^vn&zV%eeRoLvE~5;s zx9_FWzPqJ;U$gBqu-D-R$S)WuIgivo7bur!hvM1Le~A)mPWbl=ME=$6gx_uME22wy zYEJkMp^n-U{1cVy8Bxke_Nu2n&a;YsN>w7{EL}?@9f>V z0QXs?YT188!Llr@Qdp`vdMZoi)9MVN%8$?=acW%^7=||`8Ny0Lrc!|0$rFFx@Cn8J3Bgf5xEN0>4s8Wm($p zyl#oy=(qTr4okZ`uUjJr^X`sKFB9AFZB4G-7H^qr`4@A~!A)?F>80B2yB%t%&AxYw zaNZsn89)=}y#*eCPUc>ZS2Xj!5{0Msi1o(!>kLDSR|%9RrmqB)(%K0+c|<#EL;g}r zL)s|=B+I(57D!Dwi0DC-nTK~lH9WF*tqCuUha&?o{#Feh-W@rZhhN<^55FGY*5u)C z$YthX{>3b`e|8>b|EHQN&wLbKn|-Zs(Xq(Lbsy0}-8FZZ;^R;*_sH@1)qsazjyz0@ zg@UJv*+6JPGXjSzPTBKVB!CuY0JKB^EfqjLtq@2pB>U;U#uQA;1=EQcFj+lyg9%510U7*b3is|$MaFdh(SxeU+-1=^56Yhs@_39V4e z5h5NIU~4k~v#xUm0oD?u*L0SP*9o9v20+$ft|Wk3Vyc=ha$|z$lni+OB))}J!EVpZo$RhLLrH`lM~h8C-`SFBtnRo!B%(soJBU*9u?)rPH+#qrIk_j39a zCQIMQb|nhrPXB(3;O1RvI{hjE_d^TLv!i`oYWrb}wavepwL%l?%bxC~J>B!>_a8O?*zOa17cJ>uGJoNs6X*9Y=LiC z;y2?LRtyv6Sz zGsp2e$9Jm#6u;$MIjKZ|Zlao@QZdAammxb%g`%t$QT>||@N>b?Zu zamn{M455d~R6Q56@3cnQ3k1^Ixi$GB1;>4q4pI9QT$U&8#7akoD$z}C}Z|NSm*q;pPIJSbv{`1 z0nqAds>0m_dkSwJ#G7k^H>~hKgg4jfH~uViJ-~989IYDqZq+3Z6=?*pPE|Le zOlFRsTLPFl@s#)xDmuv-@hyVn{R+va6_Q7rMq(%JTLsB|X-FPVM{A#v(AK>Ex@EHcU@Ku{Dgikk(@b`l7NkRBvD#B+B z7H`l!Yf^oLPaA{_U%k14c(8`}T+_^7FYzRhj)1+&FAD3u5LZ&O;t*tg1veQUsYSt|Bd z3ic_D{o5M*3!BEivtZvzus>?C8|GlAf^7_RU(wrD;CxrZxm&~8wrMzf2%L6-^SdM* zHkZCM9nMUF^D_MO$3oss%VKQ)bm zd7|0@kPoD~U4h3YqF3R~!NHr?qU;^uafOPqaP>`5%R; zEO2-8Q)`mB9e9`PMw;&9=I@R-EA$(``KtgFn!>_kwopRxZMyk;;G?FRKY+5h`8oXJ z=Ks+)UqhnS&onLa$KbGFdAr8)agC*;X)Li|StnTjY_S-oCop>%FotB$p(|#aRG(ZG zf%qv6@tYdroTeeJ7l<1K;!~**_c34;#54|I{b65VID-mvhX(5$`{B)*`iR zz;(Jwzt>ILzv(8Oi?Wyl&ciR}fTt5pO2f}64?=|(Xq2WqxMgp^oA>B9KK_lUiShqQ zNpUB5|(U6`h_jIjVP7VasRd8e&EfjOrkyT6b6z0W0(>X^X zHuP{LkU5Jd=`!Hbs%mA>&$vo5uCiej)Q$4h&M`V1dpDM)_uzp@4y!j#y);;qZOp~@ z0@`eEMct^}iTGzI^AiZD@N@dr7ykl(--idA6FMHDU)*v0>p>`3{7dgv#`n-vk_aOa z?}g7A6dlBF;G$>aJ5%&8Qj6XvL(z7_l!GyF(fit>IV>Zstsfr2sdBa1`CIxOUkN%q z!l_}91rs(S9n^j(yg_^IW)jC?U{L$xJJsGML2-D6`k)TT0LpGCaRdeibzt+L!pI8s zfgO|qSP*=HfxV#xU_nHM`oIp(0L*$zaL@$?c1R1rf)EM@*h8sI-<1KFwLfx{1qQ}Z z799Rsu?q!ZqGM_l^N7QepOjw{buaFXj$68@4I9>6D zNGTM*r$)#W6?~1ZkU=b+$Z=kr+g{3V=%-Ll<%!aG?~uM;&}4M?Mdz~rNXJUw#igTN ze@oGU_T6Xy{bx})HB^BT)GrH<{st)zS#vPlf!>8jw!pyE{=EA~YrCco!k9W?S zq|hpb!D?sEl7+Rx!OXPL=4LJ(q|1472xEnEyr#L?B%};tUm~X>Tg@!Gz|&{)t(W!# z{@e*fI8)!UYVs2m+_y_~Xpseos70kXlA^OE)Jy|*bz{TLZ*5B&`zOT^`$u6S?s95H$s}wUM|;lQAjfm zWK*LKQRgu$h zr$%b=;ipdU*;G$M7d;Ilz_$5t*(zDt$0_!&bHyKzMY`D&^&4R_mYXb=>2mjoR3y4Q zjQfo|YQ_=I(kY}^SeK95m*wcl<#@F{>KHF`Vb!txU}1QYV(S$KM4(D)%%X-{xEO)0{Iyo_O zEIn5Sar4^R7!5VSjRmC3RKdmPMC}-}6hxK4S=Pbrxn&NonkzCm%7*}Aqr?zH)YO#h zh-ao>n=iV@#)^eO-Z9bqRO3qpPfLY!Yv@WG;H(p)AA2Sw7|djrt{fhbVKNJX_3&hN zB)+S!9L~#7v{eo{?hY3{-xl4Z9>$S$OncLn!=p2RvWLky*baj_re#nZIHx|SV>5uV zhsZd#4uk4#85D=fsSoP744{HYIt=RgmO*hyocf>^WB?UJ&0$arTL#6^Z|Z~U%K$0} zki(!BH4iEbbWV6Or7fz^ zbh4QA=el9U8feivh;~2US5XaAX_*a8M6J0tt|p6pqjQ^#HmH@-(Ye&xg^Kbbe zcU@*-$jzG;6ae};^mnFV3yQZyTZ)&1wNn-EH*A?uyh*2+GhUuE%Hj2y5~PLLGw)64 z;qGT?s(Ak*Ty$P-tdARxlwfdOw9I7BaHW)M_1R{N=(-g|wb_iC+d+CRsbxjFp9~qar!@je z%bLc1WPPu_Rt~1cSnG;32S#i1BXk`$tr)x*Y=(gKKlb9=*!77TSD=N+2&?(}%PNB& zq?OE3$3&HaG2?343F6GPd5Wh_L41c2ZLU!T;!(?TsevM{f_gs>3!6e|BwwKj+e6_J zhlh9VJRL@t&pylSD4h*o?ui`)Vtw8ljt6>6`mHmMlk~OlI6K$e2=MuL&-wigwM` zMo~6jwW@_mB)+%+zD(6ZPfnVDp?*FOYN*j;JR7p3nWb@7x)L~CsE`FvuF;diVosma?z*QqOF2sWMI`6fCV8g>jN9l04#`t$-pLB02YL~tPgB`24F!XOa`{01zXP2 zg~`AsTL2csx~vcEv<$$4aF`72^cH{x(Jt!)dvgY0R?Fppm<;TU7Jvm2E)B4BE%z-M zfLSe6yv2b!PhN-wh5rrGXjQZ44k^K;wvOIoF?IJ zo2V+Qwpp~BWAsAXldoNUPu)y}*Scu;8Tqx0Y@1U||S(6nux;>+#^_8<`OVtl41}}WAUOLwg8P(2w zCSWB#pCi8q8nvXdS(-`i~tyw$%d-N2U#Jp>0cSDGJbX? zu;0%sE5*N7F+9YQMJ&mWl8JFap|p;i4AdXmjBR%hq9VPH$Tx><*7-60+5@sAPhC#V zB~-@Z3b`rKwR$jh+jh{hBmfZQPo88Gr>riW%6uTL2aWAFdDVJsE&mmlBRo%)s8;0v#VTm_R=kU;|(D@<@L&T zH@bVUBc7#f_andX3j8^{23q*#YT0M=;A>VMmXOWe-%B5}ukl{56j?4u9>A zwO?Yj>y0(f((dv*k?nuyU`sdCAMF_QsmrZfN^XO5 zf3k5^ry(##H+kl1#+gT}t-*0z5N_wD_0`B5K0JM4xiT=|oG78I;#r8dX+L;VC|^H5 znj5P|i($zq(h4jOUmmp+Q_8KQu&qcEM;P)ZmvexGgB)V26JulL3ZbG(Dd+fxvM216 zU;Lp|Q8ZD)if?TR@s-;`c$HkCN(L3fY2VDjSO!FVn%aax| z*$NRf5zY7s3NV!o(PD3)t*aqlcWICoTvybq&P{~PAs2Y;T&a7&b(3)>$is^%rv4q- z-f@|{R$G(>B5>-F`R+6dpVVCD#u-RsC-N71se4PGUO2Dh3P(-hWPN^aEQ%2~)d@%BqA#t0Ksm(<}Cs%&ljw@$q&+Fq7wT_v!lYMh~3 zx14(QrccQ=wVSBJo54Ivrx(?@@^I3xt@$*rJuEA=_V7e1iE13dnj5J=WJVA+5PThq zv;6gd+N2>*+tPT4Dz@f5TPazSygDn3o$~FNJTloSugjnj@l`CmFyLRT5nYe(>f0%A z%uuuqV$GMWaM3r}qBGkm9~FxT-=$&<59xNw?_~gHJv#Zi6$W;53&4WgRO$o!eg~fTgc%f0O~3Rp|NJ6$ZxFt~d())HmEk zEp++$?1w}52rbNy=b*CkId-GN)m3Xr1*c=4`YWiXg>orZnN-FpX!p}mY{PqLnz3bM zQbF$*^TO>Qu5Vt@D(IzT1rQ)ua@h9rE~v8-Vka?6E<+dAdx*OBL)XUU<#HvqQ3X6B z3yMjxpm}-H3L)I~L_w7dX0!{0Gs!AGx3|K^0x366Z$>Ew3$2r=6X|QCzk~%9sUXjd zP2C+t>kT?Wduqhf2=PRulA{jNjJkecBaK&VY|*Eth;T^=B7*-(9rJEqs?N7)XT9?;)rLw+9tVi?SvFT zASu6M%HBjx(>X&ZH$BC_25+QJCeKi3lN%9g7q-_@U@ z9?4L2a19t2{irRv$tj9&`!WJIeTsT411Jt8$*VWM5R5_nu4Pbs-&cK5k7od7rzgG= zj6proJg7Lh>B|61pPqi70hpbh_%bjC#+QM))6RF`d zNpkDjT#ib`)5NBmm^=X948>EHJJZMnMYB+1#^IMOoGq8z)8ap(Q&Q&zEHeCg!JyVN z#0D}p@^qnKk#;_WjTU1qL`zh(n(_Rptk^?s_k=M2;khk(q*q^?BrZSdSkTiuj{*Xz z$WZ7TcB-3ST1#60h>`B*8@@NMi{_hg1MB}w3H}>&awZA>=L};sxGImw=wI+%eF^@z z3`JYB58s5xMgQFvomGN|_uUz}lrF`e$p9?45|4pB+XArQ-n;t1p349%xEhav{i6k7 z!R2@Lf&F&|VAizA_v10Jf3^TDxCO61u+1JNHvHKLJ!*$M4KB)KV4LGRXNt)XJT$xm zuRgFXG5{OI#V@QW@HKf1Y(@*ff;;f)1KTnKFdNyl0Ei6i1uXyz?!v1NY^w~wY;;q; zE{}n2-2$-SQoQ=WUYG$`aBm(1dr=F(f*bPc18d6wEVx9Efo;#kbSiS=BKIGj^2B=aWgNZLo?Dlrgmk0$uKTMhrSS7vBzaBUwq zmapw|8rwiF)`Kl~`NcbDhW(lhu!Fn)7&hPa*Bb0?nA+tvF*yrqg3BF%@ot%s@16nq z*##r-`HnzF&UXa1gxthyy@dybwBWKFponUOo&L=~kdQKd;Tkith< zbC+Cq7$1==9grPMQK*aAC8IJ=mIPrQtidNHIPnb2|>r(2n4u zMQ#UQwCJ{DU$-aeWMR3Yb_%hJgJ`y!pnY<6V|;k#_8pm_eZkd^+`ijFPHori>rf5p zC&rqsqRGypqh#dy+CqoP?T~R#<_68r(4gSTij?h}3k7?dvmr?+5I5g$@e`5x$7Sxy1sS^1Zr$_ko7|Or`=-;CQ`dX_82OI4 z0|RO!pU?ajKtv)qBGRkz$)34#RzvyfIu5GJfywSsb6)( z^iSUy;VL(ocJk%A(fEYSy|OezuUH!?Uqs5i!WWUcy>h6Q7&$eRm~FKb#8BedZcT1* z-G*F+!j{m!It4pfQz%kcx)wxWX~ycV3crN&Eo zO_?l?6s*(HrYX)5?aN~iAEUX`aXO&Qc?@|i``fr4YI@{720(z zaKqdiuJ#=>`Lu%$gs%#BPe4Sp6}ur{fit=c7X4m-lUAx4eEk&JU1w4PQjbYW_D(Y; za;FvP`IRg*E;>zlp7?S4kDVaz$2l~Q3^7IZOAna;fr~11G#zrI^(!hmNh09gT=k06 z*UZz|`Eqe$l-$HkdpkCa5X)5ZbWWNMNZT-&@&Ws=sIM?ftPX+djOGG+slj0=(t&t( z9a78+%RBSAX+@$4@tKjzLZ?iOuNp3y9ehIrImrx7AlO@Uy8gQ`i1_yHLlp6UG*@gt z!}YKXhwCito7;c8r;c;p?@-R1_&#;;s)BZRb-p=v+-Gs${430qTHG5W!u@1aOx}aE zB7?XNZfs_8eKNjt#dXy=aIcmlax1*U@n(A^5eVD8s) z+8@^os1O!Io%OeV!yBOM6XJ~-2oYQm&4gGP5Tb#BfkCapH|qXj11kL@g4G#71=m9} zD83$A^-f0(gGw$#(ZT)DTr}Sg?R0#D_PRR9LCP7L;wnIxFD93LXuX}#TH%V3@6^uH zlA!J+(<-7|Djg34dSEq5-xwLm(8%EGX>Mcz-(_zkI#t>2`s9#zy3H=m04TV5ngQ|6 z(+;2&$GlLMAGv6dlSk)vg$oy%F0`ghmp(aLYMeqf^hLg}R_d=&KN zR2E+{)g$Wo!pUV6FoP!>*PBq+;7%`Q15=~viWcU^#vm1}$D}9!JCv5PG zOwBCN)iMY}+!<*li`vTxv?>}bPLPU2Itl3-${SMQC&h>ov8fBl>@Yb>u|wz&ItHQ6 znZR3!LWRJga+Y$1qAzH1=j6dsZ*9IJMVLuqV8E?qlnf=4l4qFiopH+;Do-G$vOz&B zr2Ke+E{O5VK(gdnQ!W?j4}%nc=5r-^JxqR`^aq7|(s#pP0W&d5A5d0-%?-+MX4eiVRZow>_?%b_hp@DyRK) z=rRs&6T`fO7?tX+>kQ0ef$^$QMp3Yx@u1@MfSWKDwEn<>7NP#)c%U1>QZbTl2OqMe z4^LMk>x_Q=nQ2xC*z`kM<9rpdj!wx_uMk=B0UQy~0;Byfa<^B+_{WgtYnMs}v=1Z| zGM%(!qor9yh*rtNePF;@XDDapKryF8#yv3Lt^ib4Qx-Xg>mfi5)0}LSO`IBomL_X5 zyMEIPD+W?)si|1OUv7qHM{6F94x{oaf(H(S_Aq&Q(}7`i`<8l_D`K%RsX`XP^^vZrt4yq+h(}#rk-k^2 ztxa1*a_*c`E#S^QGntsX_xP}Fo+L~}ujwQ(xu}mr;It`1LEuEaO+fH0zV#{;~*jyR1 z>b<)-8E#iyND)a+SHxR6lNrE`A~AFI8SUmc*m2U9`xgwH#h~zdh8L92+I!bJ#B>XcU&sSF^krFST z2AWf|;y%g#PYCien8peX)S~SQtQt5MtsInK!TKd4qRAN}mK2e(9%FsQour`gx0VDH z$A%XAIQhba%AZP?ZXohME_9vb0YN)Hh|NYM)zvD7pC>aym3lygqH|G|-E9-OWvJr{ zE*ai>H?K-_`XG%Z6RJx{5ul=;HkJm(feg=V<p$h;-`h6B1e?~& z#E6Qs2qcCzsef+UQ>bIx)e3!_{#$7T53fLRzmwb{O{VbndFuLMeu<4Gg^w4ABF;O| zF3qsBKuH=X`Oz`zIxr9!BWTEKy5V|WivFgl1oxFZ34MJZvZi^8Enj9876HVOVfWj< z$AXT=V2*YtXsDUR8JCGys}q{T?&{=jx?EwPn(M=O4k%OKZZ}kR3FQ$`P0T|Kk?R0gDaM~ms~!}E zk~(Z>(>Xy{F=ozE<@(SJDjbu~1#Pg4B4 zhsc6fqBz7A3fDw4m9}bNqQO{00Afr8+)Z{G;6Ljadh2L@A}n_iplZbKQn_4w4@A$5)DXD8ORv*Y+uoesbYC8HMJ%z^u#fJ+MXZ^tNq77f{?v{ z1jwc^)sVzw(nkJDD`jD$H$p?{Lnnds3c4DYK->k7wH>HU&A#$DUE1eZKFsa(96x~4 z*}$xA7p_S`(}Q*=caK)Gq~M}(lo(<|1t6HYx^8Gp-=46$W-X#=O?!eKe^1N!`)KCz zcTtA%_j?(CPs;dvFdTnhqVcDm}M$_OBm$J!*5_ohV2~K`S2$dH zXS5KCmr-FbkJmPI^PtnlZfyIgy4@9_j6Ld8$SUAqdFreSvkgB552}HV%;y!Q;ss8} zQYyobQ05YwWeo?VhL@OcReDq7awSjZYFyGBXa`ffK85d&LS64B9EE$Pj>6?7QlZdu zvspP(bD|!M&&goCH1lBmOoqYuj10zSWiUP*4#sD;c6L5#ILak$ZZtW%l4E>B9zF!D ziP5MGOjAmjRU>xD0@Hh6(_)I}8x=jvI%~CIWyhHV#|1m=)h9F~8fO`0vcl9%ABF`Q z#n*gQlcDv_fs3+p0mos?TH3BQq0|Rd1YST!IIPZm$t?Wdm z_L;o5*ll(0?i;393{x(vraoQEl!~H+wu+c;v`_78y93IW5ZKm>MJ|A`T&GL4cYPpT5 zv0?U~tSVGvd{~93F1y-ne8}GU)0`p?()Il%bd?ib%QQh3M3VqjUn%*`YC^3xkoku^ z1v)7}GcC;!#YFF>+1B)QFYW1`H^2X=`Nwvj*t=*+|C0F&7o9l2e?f2G@unJPCM{zq zh)ueDuthq2t@fO3XY>v^+f(jF%v^4@Ed#5{F!;$_KPU~d`0-OZ_}u*5c3WSapau9G z|5kJB@JoAS=mn}f7->4L+o=|JowoNI4s=Lhyx1g$y&~9!FDoCcbL*_PSqBQ!}*?X-S#J6EAOWC0`lOjjC zmF>z-#m256ZEmyjW>r9KZ?h(*R6inTb4AwW#|*orTIZT^lG)?DGPBP0l?*!9Hn$nm z`!;vbFW%$)Z9?ZF*Y_S?g_5f5g=_T&Usk27UKvom$e;n|Dsl;=OHS1qnamtiZ*Hx! z1Gu$X)yqw-rSQ}QZ}bYkkftVFC_ewct3Nc4(+Ld!W^AqL&?bd?>qpf6U!~{NWH0iq zF>1|Wq~nlvADaTxQg7V_UCXNQrbe5!@4e`bq3DVz<`xlo0}ri1U7%| zt_Pd6)Hc23@!enM!3_vjcwGEo8G&}cs<;*dw-fS38Mx)MJXoN;YdYMeE?JW33asQ} zGpW{+i9DSHTSnk!Lxj!X8ROksV26DBzWeQez<~$7;ov#=5j>E7NN&xb*&nJG0>ZcS zzQUyk_Y$qvwPv%eaO@44IrgRu9J_~bY`bvm??R4!37dA+K_1@ritoEVT_oWWeG_T@?FFo)TKh6 z!kR)(kCb5p#b#IqHw1ZGZ8cYY18uwyPV!qBG;l>S?O6`PlBWctho%1JrCD=h9j$HD z3!H1Q%ox)P1cnOdLWFx|De4$pRnt4@PF*^Pz#qu=W=bt)5W%8oCMkuxp9|8*NtZhQ zbf(#E^B4s0u<{WTW!GmPI6uM?SUr86uZz4Hm5AR`w28e9w_iL_^qbEegC&v8=I zA!#2ICtv*&BbEFq6NO5iWCr=V5HBe&dfiGIG3LG3R$Am7t)0eneI}c&3>&P%SH_^D z3^cHZ;@)<9r8r`{FR>RJTSK$l?5^y~l!@4tHoBG9l|z1lJURhI~ZiERKF`itukxmjZ7eFjY-#z z)3H5k(04y((XO&}D}QIt5^hQ~!g6lA@rt&^xq{3I%FE0Ny~+rLxyvdOdHb0^8Z%gG zQ&+TlJs}I*l{0ng99^YJKj0NR(5lWfTu7-;fh1_B(@lOuD4k3WSuT03_~!j(>Ro7@ zIB!-RtQ6SMwpJEH#V*>yEkkQ$u}f|w+CXmPDG5b-S=a6@60KHUfdnDRK}AB~=oQRs zMB#fdv%>dq28Hi9sCn!beY_}qe+U)6L&z<B2#2!55DYZKlf`h`NUfiM^@>GXo$Ms%^A!I6*64Nb&;TXR>C3)H33%L)k{qg|%><`vsGS=%i)G z=O?j#>7+e0I&3yTu9C%K+ju2cs`8l;My`5Xgtr;c{#i6 z)4G?d{An|VQbawMIc86$- zh)Q#{Oj~$b)#A4U2S{|NN+5A!@1ce5B6)|6heElsXc$1jp=dLua#W_+%9V8Ap>ohiYJm}*~794t=7zQA!CA1ao?IRDou9t(}5h*?5VN7BWr zYGR|k5O+WmA)E9FU~W@KO~X;LiB6o-P*IE@x-mYFW`jmr1jSO%&(R$05s-zBl6gk4 zAO<37?jIi0T7`Uyv1HYX8mCM-IWYq51fMcC>`RwPM>B6oo|T9F9~;p2dJEQ^*gQQuX6bsj-WBfZmY;!km`0Wb#;c^!YQi z98d_F8`jA)ejPI6p3OYs{*htC#WLd7$%y-NIO4ucBTidD$O%Ab#q_ARh4g%kI6asl z>k4E9l6l(Qneq{mE~%CsM&U8vx=KfrTT-Ulq$eJ?IpL@L{0@y;`(0sQv7(55DZ2Na z48YWN$%O_h=a{Sp`5+@1oM9^V79wyi+(g@5(o@;y@=V)cW@gU55B4g}s4|O!v*-`K zkenl9Z_CGM=@-PfN)KeP6>>JXF=D-py$v$Z+Ey1vUS=`ieDLlL-haN}?bI(rFu%s0DE8qyGu5S->_>vCy zo|r3=FDp(ZQY>N$kVIN{bq;Z_6R1_JiKbGXBB|#Ru?@omXZEF zhZ)Ia2v-nb4><%PMnXHm({~V#`ty`Ip5CmSskggu{E>`yQOD_&+KI>lGd&KOIqQsB zr1&!+_av!YA*crgorld1nsZ`o3VOR*QG=k-@l6k-J8x)rjNsD!M0Nv|hjk5;ma)LI<=VFn|fGERsIb5aUGk*=C&_!+%h&1 z>vJ74Ei7dzWez$fj_rd$O#zoVz>*KqB9`RMIj8yT3hbtlqM#GiS&sjqv-}5(pnXkd zLAz@PL3^PH+7FAM{d*{Azfw!k8l_q$ZJuRl-=RlEhJnH-haG2&7d|C&tWQaRJ1p2; zoP4U{le3m?h}TCVu_WWDBh)8!r-bSj+E3TfwW=MblQD7D-iy)>^iiOd*>%2Ao~-nx z%hLD;CXVn!4V#16kC;o zxbum)Ogi}g?Q@9cIdV_c+dk7(OLmV?7d)~WbMKt_w z7Z_APbMqdZN`U)%+RWEfkjPLw4hNUN(;sS4N!?j%$A^p1o|P2Z`i72A&!9Nmv{tH) ztjEPJ#s}mISaX> zs3AS(7#UNAg$3f=B~%YZ;D(!SDy9jAYzsoQsxS(@rKRKL!F7ms;zl*Ws@6ERr9@;E ztP9YOs`nVE`k8==?k0_sv+N!nLlGJ|BN2XBZMmd25X*Sa=p`j|6LV<80GFvdR@L4~ ztYZxeuF=fgTCs#?{?b~snH*JcWrYT&ya?-*teCM@;fz(6EoB;8y=xJ1M^L94A7Ml5 z8%(z^kVUf}TMGUa7HX7F9l8vII&C zlG{@}8^T6k(vXii>2fG{=H7-I({9x`V`E2r3%!VcfXD50JHXT~wGMt$81-Uxqw@0k z&F?7tkMIrux|M!S^)E*t`f-k$hNF=~xiOrKRMIWkz&5&8VK6^ZE|QBI1&SJ)AWA_H z4o{HLkfH^0VRVI%E(R1)*9r*c3rL7E<~X?S0>GwTSGOIV{j|ST?C;+g13O&}ftwMg zwpd9-y@L{tZ;S6#|EbF_cBPI!cOng+443>EzH@GfXmBqF-$U{{eB<3i zlH&`?{{>93sVYrk=W8&T#;nu4#NO^=c;>H|KgFhW3Rcp zPi2ip@`b)UEuUI%OPsavx#T;$raG(D}YPbrS5kH%l>>2vq z(HDOWNnfW&-}uI}Hs)B)(OZV`ISL>_aY^ZNs)^At96dsz`brC0a;VK_#q%WZ5VVUn z2qq{JYLRAIC+P2bx~kKBtuExDuLUgj`c0T#k|HVtp8%=wJq#3kB|2{DqP{saB5C@j zTMr;}b+s^hKyvVyeyJMMyQz!(u0hKXikq4-?@%Igs zqDa=xOB>S>>T_jaPy zdW4}=ZZ?t6X|L;juGv6jOT2B84YU$lXR@isd;Zi2%|&jU$|>#i35-@zSd}W<5oeu5 zeQD!d&DCFBN50ggK+#&VSj?MQZVxn&MK=G!1h%3Zj7Y($>97v`7bE^stwQmoXs(`M zvz-Ug5oV`&#tEyZ(D&w^fB$c&P$lkIRC7H>{T@AG}U8pu+hSB6jr8o8Ei zC@Xww_un*csH<&GG-U22n^PTHH;3F6Qdsr!DT$?^n65QI^1P}y7)-wx$T zOkkg%e&mZ~Y?$v&b1iM%uOZEphJ~8fRe|GV5*e=GSweds(UcsPj{b=7}cgj zx}*S;iB>$Yk}?{`;k-np2JmO5a@9?$KpKkV`afPq1Z7x}&{|qnt)uuWGVi}!&8M@K z=v!yZGg;^TsaM-pka>m3z{K;KvbQv)*r+K^5xQAe_2~X&5>peniPdOrvAl+unD2>Y z)nISclmk^$N=-NA#kwBVDOv$MNWhmJ@Xt^`kOFuLk}7x@wZ&Hgujy3-Erp|XBhWmT zmC2G-S*y3W=-nW3YwvKRWGKZQsNkYk(iBacU|kB%x0Okz)6k-5-9#r6_)C<3iP|kg z0qVj69{Aixbdwkv&B(GdG+Rr0y499yDBv<=trZ@uNP3ncX|-vRzLaheQzY<+dV~;l z+##yV-0LZDWkcE_ki1X4)J6-2%*{SWHTzQ4?Db7I`{i_`cyOH=+&^^~%fGm>JlCW)w$(|d$;QeA6z`w8u?J>o zEYC>XSpLP0<+;gi>{oglO3%d1V?2PSHSK771f33RVa~}&;i>KQ)R>~R%N!LSmYM2@ zXP`PySxj~Q#Z>3X%cXj?wOK1kv!Bl#T4R6&ZW?F zX16BWDKlSuT;_Hz$k5JUe&lxY{OGo`>4~w)#>t!+FHSa2@1y>E1sAUVT3W%?>zO(W z`OJ&;T|Je;S*)uKL#;TThHh567=B@iP6Jx8oV93IjU7FH#RTQz(a68}%iWeyn%1(h zJEvEY@vD$GEb35XhH*T$_lNI{j9k9HL|3cYkxz{IWG>3 z%R3Q7g@B9Z#TEzC zF|d%eks@7hOk<2Mquh`&Y`6RsdtpqItB{eaJ*>HEJH5l&UgyMLL!sE$iocFusU2j& zd;@tH%r^zhp-qArp;+nUlS3ArJlSFN7~?&*ZwZ!fXTWm3VENAUu&g0#O?i0O$8v*U zxiJHl?+KQhr-ubMnv;>i$8w8cq4j3NK003*=0}3%kS66gJM~?hkh(bZp(n= zcI07hxkHc~*(4HiB?2B?L+_L3E&=qDnt-f)xkmuqYXHT&JMzUbj>JDl4i+S@a|FqO zBqwtIZk;=1JaO_%lu%Qg+%H)87mw?`8xUua@v9E#;jtFzI4X06^}{9r%1*7HdApwCYtOu+djK^y=n5e zsX6YneP-AKaxvb398BPSnj)~R?R079o29mWYt;5uscrujYdc$NJ4b3ez^P5!+z&$1 z8}N8LWvi)b9^4{|3k1cx1jQj1g^UWO(qZ(%9Ru-Pn?-OSDh(JA}7SwJZRG)%+l%E+V2{)@d@}g^J2=f&G!WH&4PFd$_nVqh?f}@Mx-<1F%(c04KVSc`94Xvbfq9ZyL+Mo^Y&hX*(|AiSN?_!$AQ zu0;_4Eg=3WAc_`*2x?~7l0jjx?Lp_a;&Pb!*s+v4W>S5!z3{{Ij;GU8P)iGB+ZH9T zGWTNqVuh=MvV!hs#N!5qhj_xI`q*9}*w(j*ZAZbjlVICW3)`eYVX&QsoTrDW?1KDe zlj5WfXw1=M6QcBVmL5HpR>3R8L5yv?Ngi1!i1XQP%D5Fa3j z4-~}bpsWBfV|%+nVGuKo-(gaHY=;T9b6doAq+sh7Z0FU&_D+Ms!!{MBUV!i4g~u^e zrn@e9Hxl22$8o`%_agCqcr4U!{Fa^obUX^*k4!D}!g7?rqwqxh;!(H}Wu^7P2kC{Q zUi8J>rVk9y!jXsFT!Imc*7L>INl5gZ$5#Lqwn(%<-0tx&3>>ZQok0#(wp8f7V}8X%uV(&y;G zMD?eHHw%c%S_E;XfH+G){AUsbkFPJJgLu1uxV%LW=L?8;3WzH#2!CGsl0jhz#H*Vt zk^SBDHO7qJQ|}jiUv3fKhXmh+g6}J}@Ld(+yBaya7N)jiHvM{-dJXb^1CNW-+G1=b z-$afUSnjjZkk3g&u0>hFK>jw;uA_%HkgrGTcf!=~BJ~D5uBZq3M&xJ#@>K%)YJq$c z$_gMehu%yt9LQ-)82?Rfk+74;p0`VM4j0rlO2`W``jC&~&?ABwb} z(8EK`%lo_i)Vk~7_)c2s}J!w{w?0L={G*1UV?HN<0CC%d^t*BjN9WE zV|>(N^a%AkN@uqB3ALkuc)UdruMrTt2#6;v2q6@A*B=ZDgY8M={9~Bg6*FuE0D5Gq0N^EQg#bUh-ZYU ztW9i*SI1MC?&x^|61T#mFL=Y+%GP*uf_~$-bUDy5E4&byT4?FXD1lkwjrhf^@FJ9z z*3xb0g*Trt^KNTWeQY_w_Tm+J%S@_|ZB($myhUuM2)2q~ z+dc&wYY(q9C^WW_Q{o+hq?nBjukusVRHhwK$xe8@nJP|7hcl&VJ0nvIEOCxB?LVYx zuSQt`OK{V6p%*SoP}6n|l47<>yw;@pJ@GDq8=;C8;J#1bzF*+(hO&IP1Z;N$!LZ04 z$o9H0^)VRHUhk*Y?1`Bu;v%Xz*%P0Xrgb1w3r+j9H0?9ev{@+2Z(7Vfv6qjyj~YBX zNK!p9$E5l_@kN2#i7Hxv`xSxvRe`%V%1VQ~uR&qQ#C}0i%tp@r!&L5{15B#li0h;g z2U3Ps8gYX(;znu2K`1M&5eFL-wh@N}N%1Po8(m>4tCfeERKF3oNh1!U46QWcPHDtl z(ulbzE3FYn7!@_?0xGhcdL%hzF$+4@o2Dp)9`<#OFsF z2;RUx#!sy|gpWm;k5d)NA^fB?q8FK37!!YyMm!~rI1Xhc8nM8K+o!mHp-J_r`;t$3Zr09 z7;LN@#9`_>d|wPxN0C|zQ_Dyl!=sPtclrMmBv$Y^A$U_o;y511(T;vnBAp~unLwsm zU2|@H7=122N7mDJkUc@A01|EeG9d@^+#btLZweDs;J2gLadb3nGTd1&JsBk7qO{2ow=tYtWXNRdwg>%AGrow-Osod7LQ)-R2 zo-J*C2Qp=Et9JaG&`pp30W!Sg52>thkr3e>LWFZsT|k6?k~g65u=6GI#qd+k$9M0< z;{qxxIVj#M4Vr4)pp-%JLBaX~!TLe`N*NR%Mmi4)b@F`SjLl_K?2=B3FG%qplj1K( zAw`XbzZ+RErZ1BXzZB&$*S@E5!{ZKuSDS0E5tN@3l<&1DjaYm?k~Y#KMPy$ta6T|? zI5!BKD+SI6lW;CX(udRGTrF@uGHp1w3Y>2UoR3;KdM-&*5m*tp2wA^PU%E1h^`npD z&3E)0e;U3?@PA_3`0o?^w+Q}=6Zo}5*Qe-(Ba>>j>Jk)iTUx6w#hW`)TXnaz>eI;7 zK>F0@**{3DekQH@3`+IYjL#wIGJ2%&?5_mQe>Mjv6o#`gx(>wSFg$wj=)+?rJw)hO zC)~ZTq+%86L4$qjfM%R7?Hv?SBphKs%jkbUwg$ydEnwpn5!nt`Gd;rSnI2R6YMsseZp^dOqd&i0k>S`n^7a z_$@+%Ek0&wa7;5G=(1zJlM~_Q>99g!e6mXMUkv^Ig(-4wL3!r(eY!s_P>#B1Z)wkcc}2(Fh1uI4&aa51I& z$H$BPRl0eXVp@3}8gGa4RlLT`9r`{&_i{mZcvDoXe_BZ&i+M#;B>Rwn+Ce}a+!QE2 za7P1y&S?5H+A%YYtnmDpz}eZri9es5%`QjM73s6tYw;l~gkN-KvrDPql#^9;Mxq7v zSpl`XfcjDr6z_z6IUQ7ofcnZN0(GT;nk}Hdngqp0)~nM&?JJN^f7t%mR^pyN#K zwH;70@$o%UXP?x0gHxwIopTzrvYS!~Vf?+m=TISWMlKVU`vuF1g5~BU7T!6yB^67& z(vT(o!L;E#CUE)%&JPoCM&23Unu^8H<;T+o@@E0ER)E}=0HTi6-%%IKozuqhoM0&n zmb;Qz?ncr*saWE1>7#q64QHzlQ9Zm`I91^MGzsVDNcu%OoHGQ@eba`soxnLu;QZ3U z5!34ZNc*+HV{8ZyAoam8^&zA_jE8TpPFsHVAWp<@+`RtM`CZc1-y&13B|@gr+AeK< zpS1N6lp3sQxUG-T3wKQu{|-4H_fyka#I1h@HI z#d4Ei*_<*=k2I?V%PoRs3zX{dLK-i#$!kkwy_LRnJDp8lFTk7s(r^5(y-V-6BhKJw4`_0{Qf2LJ%dBj_P-wrmc0c_qi{n>EIo@BE}Y-DY}#mU z6Eyn?n&#|%pv;2#-Sc|;j_E(Ccfq`#?j`f44eL&Ub)dj%&ZY)fy$csF=;$1UpZn>Mn01lhrYta*Yh?_0Wj@!~~Gmd&5HX<+?aU>z#3n&0S5wwKbl$PuCxWG>JKP==Fx>*?4NZgT*#rasw}NDzAZZlzEIII(Evrc>m<2!6vf<(fm9 z2emk9SU5Ih&QgK148Qc0>Ke;VLOS-z7G63-?bcl}L*ZC~H?^kr^CnWZ@-~zB1goUp zHzdh0gR&`a4SyQX;Ugx&A+*dsX8);QMCh*vR$0Q!7<8cNaZ^h#*JkG`A-FSQu zkBjiQ6pzdB_!1sp!{gg{+=R!E@VFC?2k`hK9-BQ$kFD`|2_8G)u?HS=@R*0kB0NsS zV>KTAc;xUH#3PT#T09DPtixjzk1`&o;Bf{$yg*t%eG{by8-~_z>+`qKTeko3FIEbN zLD-Zn+bfVaOWdq0k&%l!EZMP>2#Inyr6-rX#^QHGy`HTT4)cSTQ_^Bicoj7`~B9wq&M-`7=hH$Nc5FeQ&0%toal?d^7@F8<}Pg8WTAP&6J z(BUQlG0%c9Ry24x8wf^&-UCK~W03b2`qW*p@Dlr2y!lb^hL`2Ncyn9uhFwyR!<##T zH|&ynJl^~yc*8EK3-IRN;0?Q^F2tLk2XELVwGVH?1sMDNF2bAp^*8=9>mf>2#9oX{ zH6>X%f-JE&+)CeZk3WL4nC(wMS^lz$h`p3joi$aTIySM)r24@AAi$Ql2y95W;Lif= z#8hA_3(uMnz?peTPy%;Gkt^xEQfU6w}5Om3H*R@`1~5KJ}FJ(tkNe`RRX$XYw{ zu95m1ML$l|uP)-&18vuYg=0^N-IObe^o5O0lG*#^dk|g-P zDa8ypF8hZjH6Q<&e!iVMayO;JeobY19mY+3$fWv>c|;oXD1JGO zN$s#FOer4up7c}GjASfooXJ%N+H#M`@pq} z{N}fN1WaoVD*;fT z4&JcZu?ybp8oXh(V>i5M58kla@jASDeej0Wj#+rKXYhvAjyZT^rar?Staj{!H~Z>u z^6>%mD?_)x?#J{MPZ4jx_dKR67;uT%ZrqGhQ!6WP6Mkwa!vs4EZ~B8b%rE23W}CC|CY5c@!JD=E8=uJQ zC{+>pKai;fB9~DDbJrMtF_GVnvJyl-mr}D3`8<5RUc)tf#6&(HZzh8`Oyqas&6|Ta zOynuNd28^7iF^Uxye)XcM1B|E{73MHiTrN7NhR`o@aBB|jZfrvQK};Hdy%OHB43Rq zE))5ED2s{wK9rRp@s*f*95P+1xPo%a!J^^2`AicOL0$8ok+8~w-f=?z8#GLJtbP#V85SO+H z;!P-lS*l+^d^!aLl0KIXVn{$-)*^@z0Z|YT|7k%u`tcVG1jV3fv*zXa@CrPPMDd4b zMc{rBnOcw=>jmxxf%_$tm5>`>rc_66)P(z$+HlVlxL<7%?l}VYKLqYo7Oo!Z#OYr% zXuWYBpDzf$-Xel`34(VEf@>@S)4%aIk#?;?VPw#^kos+t>UZV`1>1GV)k0@}M6i8S zuw9R`{LWO^zH3l;*lq|@Z^ZXEnN%O~rGof-$khn3H#^L&J3G8oTEf40cDNUHq|6R4 zN8T*6!z+a-x1gw$+2Mz#ogUYi+2Kc78d-OC*u@~2IgYl-ziB3iNJzPxsXe~c zw%;%;jhWj_>kUV+a{pgp>h1XWjxco;R@^)N)HE4UM!vgH#2kX-F0S|_`A_g>pWsau z-`tHic15chfgP(4Lc@6ex(At>;={A+8qY3*{=-uW|6)Eo;v`B-;lqoOHwzzLA{yGy z05^pXe?jRiYg75~zATNb%ZHyg2pVcB%W`Wr6(d^P*%hYx_?MOd4kO-gnr|499WsCI zr=~F%`v*OMGOwm8T;^gI%?I)3n&1t4Up<63*XlREkh>mWnYkWDrbf&qbhpZpwL#s8 zvY3f}gR;Dyrvdl~B__K)zC{o`+9HAjucX?Tjc*kMkEJ1aJRQLug5Zf35!jXX-Gbov z7J=^kv_9o!^^?f@Gy2l)Q(p1?5pV9(Z~UJ7wV?h}i>MzE`@qA3`p*f}h)?wtr8?SC zU(77~SCi@kdt87$-6F8x3$P~z*xyotJ!2qvW9eCw>H~XPfIZhDuxA9=vjXfNNnr6m zk+vC0Qjcf|8xU{qr>5})kMk`chiBZOc*1YT)|9Ho>+n_8Svs+s+aicP1Vp=l zcySU0dnvv&9mGrlv0aNGIt9dD0^(&BgqdjK9l(Rz8x)2>J0kTJVd~CEeWjn8#+f{u z?10J+q`F;cwF^pk72X^iym>9&?1(pJm1nT*hBrImP3X(ZBVcE|IU*>Hy?kGdH%I9= z{@6I0Qq|aa4KlSbHjYCHJT{KUFCH7ape%oEkW_pvrFsLL1II>TD$B~<{M4GnZAVSZ zb<+%?lxs8Itk7@#My~=;p2YV+rWP7KfD*XTIsD>AzYb-EllV+ZgKjXDYCL3 zL4~;kh!uhT@a9bY#&5_usGR$Fe`IQ*kIzL3+{fqP7x(c2D9dB)G}MgkAY{EjV>6`Z z_Phaa-lO06s5hb}MtyLLs4qkbjQYd)#i$QSpoWriD5W~Xv1UUK!`GkCaJ`1i#hXv* zH-1AtgUY!fha*!X@_WXBXX_dRJ|XTd{EHiSEb2&EgnbHmvn;|sjf`AWH;VE`am=RT z9!gIRWMd4Nm!*-i7)Ub}Uv3aIT!bB!8y_6mG}eGG2`#TQ)yMN40=UcSuObKc=rLIu z>-FeJy=C>(OYM!v#R5g#&=+gp^(WIOYf)jcdsm$O;>|WYwy<_&PBFheUyPK~Tr^y+ zL<GKjFh1gxoiD8~RLZ4MLSJngAE}fl){aEuBl+m0{FkGN4`?QHlKI$D(@kS!b7{Se8ZEl?MAG>VXVh4TO zc$qRNY^7W=;)^qNTc~Bn=O?4V+*oc+p;#EF>yo?LBKkK+XX{zAe4bNQSESreW=EBL zZfJHiF*Zb1&gRO;^OOi@nW`>_qa%C-Da6Z~OG+D}x76z-GgXPio zfq~9wStUokgKCSp>PSSaNIX#++=@Nsy}fy63KF(HJPgV#l{lYuVrUQ?c+u|F`doGweA(~ZlLX#kDT z*vwbB3&)BRYYU~l$IInnlpCk}(AP|W^qo;pt^^XUA@;B4hq$=$GCALj(1$Fn1_o5; zPkwEGB~xiKr%`_?QfU&mb*ybt0nD=qRNVs`PT!lu4L7 z^nv*0KY@aZ1``_IBnjKu32x1IDapDFMC5skc>S~jjow0u1aGcbIE@e&h=+OJ2qkqy zl7F~Rsg9$+Xh;-`7v+1!v9tY54ze8q62kx`a zESfV*Lj{_dCrGfUmT)0MZD1eFkBd|Im;~_lj)=?n}71WR?X)sgCouy?S7%=LG_7JQNXKeN|PBi8qJmH zo^=q${g$Ue^(jw;dUmvGk((Ja^Dw4u^}!fbldCef@L7duLj?k?Qe~l}s1!lBaLZT& zMOA&zKci6+=>?i6oyIBhL-AsgX$B!`RB0}DhL)b6tI^^~V(HRMQr@(XP>m&0SIXmM zqJ)|0)O1RnQl$xeFyA&<%;!oIV@zYNh6*T=B*xepBXLRqsPE^inOqI+J@B3nGIb{>kqRc;vF@j z6k%0!B0XaI3qyp(A92t=XxwzP=LQF9N*r&W9i85eQQF=Wwf9ihlXf317$0gsW0sH? zr3R&_q3V{Rc$KO|f#alOj}bfN_eHS>9(drKefHgFAGgfiqopLjz$1gjGEJ=1EJWKw zb7-8>xb_q`MX^2iTu7WoLaSI9&JRvf&OP^Z*+5~WCI_9=3`M2UCEpO;*o&KqJVvNz26xxy#+0Z!`DX6Ai)NiyYwE&;m zDXhWC09(maqmY7izE8p|E12a*Ft_S257FeLG47D4L%(H!jr=NVee9sqoZ|&)J(V#| zb(v+)%33lRKsy!QPWPzs>W%7PMH+LQyPnty2E3G^K}r*3k!FxaM_m%Flh13lZ&LNa zq-x*J(!NTbXXI+97KU~3Pi;+y7a=Ki2fZqR!o21lYIIzt=GRUWc%wvh&}pgxr>O>P zxrXNTI;7@nUYOJglv?rAlx`NKeC?p@QwYyc2+wH1=JAcjg?H+eAq`kp)W{kIR<7}F z64zT4uD3M8^#j3`7&!;D zzAd;C0(Hn}*&1830@e9GiSI0h@2p1nJ}USUbYr!>MY`#4lepfdaJ?-ZmpIBywe{1Y z$`kfGhurMuVC(%7%*EPz?-;LF976iZF|U%EMeFt>U#c+BM70ZX+uYEs|-Cfi;9()RSggDDsEO7C@$AUd~h9 zU1TAfsFb=Qtb&wfVUD#d43SmL)TH%E*nMD4qGcHw-bR(#SR^-<2U{YU_?n%p+BPc5 zb&l${4=Al?Sw^kL_UDWHtAn)I9P+w!mqeHPnYdS%r}yeq@5zOHk=hIbh1A$E0%LE! zRGna|=ZNl=O1`K%i|b&sE3ddZO%pFvO}t1o@uX&(7_TwD1}VA|Vu|M~qSvlSZ_|qX zh*ReyCpRlta1vYe@VbbM7?H8mNf+zMd4N~IHI}%YZLka3H9uQ|3MH;=U;yhpTClGv zmIv3-;*2a$rP18W|&W6Q8((Es3j zW$Rmlu`%jc`_Rq0m=Rh}uF2=kqD=blldAu|r26lTP4{0x`Y&Z!l0Vg1maMehC(DvA z=V?XaElIwbNV!^4yp_e&Df6lHv@TbsI&Hi_|HIg4%;2m_4Pej{Yv*&+UB=u|?TqG& z`HKyhWU_376@_w;pb(OMS&{7Pie#&sCfUWJt|n#=MyF}8HKPtmWg|a5?0HW@p@x&}E1JdPO+H2r03oQlF2HFB7 z(=TQOhp~yTA*q!4TU)lM1Y?!bwj@0Yk_H5`fb)zmY%1`~1nPTZ+{|KXF(#3OIY1Z1 zbc88DO%>lzOm(MXs)1&iYU;)PWMdgz=dUD^Q-z<2r;1!UpG_UQ1X3lviy~qlrw?ig zm!GIc-K!ck*mR?|87@v#N6f7VyzF>&ApJVMEZm?Wr$IWDca6wID|X)E|FHKiaB^j3 zy=W9f?1T7#tB9gS50FksR~|Ei&@&n7>F&%lB%P3SX5z`juIldUq&n4AO;vX?O$Y}B zWzc|tBZ!E4jvxYxB8npFRe8)sMMO}z0_t@@(4&H{BS&86|Np+VzP0wQUDc~P$vAT1 z_ajqXwfA~_>wCY}wrvMEhx(}C7gjun0#jXtrb+Dws?8cT8yDt8>~nDpu_HjT#LuJw zSpxz}>yVetHTcz??-eL~j`0_|$mwdMn|#<`t$?s5#!>3?-$84cXINfF7wtp?g7|WD ztp#Oft%mCbi0Zjq3XNc2Bl$#Jz4W+Vs-@!+b8`@0)PN$R z;L^iRZqHG{cDNVBbM>#f)_--Df>w{#mOsF`7AMB;I1V>?=MEy-N}Yl9JC4>?Tb(;H zs~F7v?o$*22No zm;a0d{Uioj{xCkxEPsT5f0X|SOrP<0_$0Yo^66e0X7b6UFfD%&K;QA4yO~IIj|{Lr zhew{t*ESjY;p={A`6NR>nhsMx^v4Q^es;pp;cBEuauON(o5Ik`AHz!FIsG{P^8(jT zd24ml~KEM2t z!VNw@VT0mWmSY*V4c^7~6m}^a48N#isJ|d#QIF#=vZ(F0sBgvix`qzoaJ0_6`p9?2YHBv9(Pa4ERR;c+;h_m#vr3L--OL|Q}iBcuq8Dx`F5 zfYC5MK!)tIUT#Ee^`@@8m)&KT(*btvnXen#b5G&E!R(b)u2$Jt z<2;OP;#q{Q<2{}ti&^Z86DabHXt1pmDW8N7zJ%|27F*1Lek5UNNoW^h{}oOmL%%Ex zoyvihf0b|Zf~#fNdw~lTFYM(B3;T83^V@7;Ke~m5{iZGKw>DqcD-ss=+qSUZv4#EE z78dq)Ti81`U)U=X7WOV%*gdweAK${l-fau}z0DW)s)U980JROlu|Ked{lpd)_J_8x z_inzhpG;WT`#6j&?2m0>KedI0eZUs>C-K6HMeu)5Sl9<`VXz4uRbG=5!Ixe?Ar3n= z>zC@MeQu=pgsFeW$woNZ)|g(*ge4P%6OyIa|2;L#QbiyvF03S$E_WDrt5hjebCwFnRP!HMa~(#XLUG!NuVPwtR` z;IfpIJ2=K^5WtTD{Q&Np4l<$O!9zr?Ht~hO7Hk)795)yaUkP29^GM5V+t;~6`eU>ot!u+bzX15Xh&yFh% zfkj0BXl}tBJqTo0o^G}}Q1lSy!S#pKIrVu*fSRy;JH2;4B~-FEv*8xOK6qMPqN8Rp z<1$4}_oZ8L4H@GuPZtU?rh?uhp+;p0P*N>{ZIO9l&@ADH=pdj0HS=7}s^~!&CU?^C4e`x60mJj>d5WpjodYqs|{xEr8k{07K>VXe|@Dp4VnW*nKGmS{-{burD>`KHW0ME=#T zv2KBpBkw$hU~13F{DDo3hnfWEB_Jn{_IX3RW3HsS=6P7rc%GY!Z@^6T+p={4;>Hk{ z1OYFp|A>I#MBp^gMBz5_f)~yJ>exZMvrAZsS(#V@60SK2g%Ert`Ge7A?jo1O8ao~~ zG78UTw}L&%=6oITF^OCJQUdiH^_hgr@=3VGn3Rb_?K%ZFVKHI8C1L2`);q~bWau3^ z!aVV47!A`_?jSIX^B#aJ$7;}cn0B3UpzM;GCM}>+7VN~UA|JG@T3hOgB-u8g97jQw zQ)Vo4ai~0|#I984#3j?N;Sz87c}B#=vI8_Gl@OX)K^4(g<4A%t2uvJg`c80D>7@PT zi1R9OWk~)7QNXX1uOvIHH3yo4KxYm2?}{Kc^`9}y;R(fu`NptO5=JNm%WF1~dY!Q% zMqHvj^im#$sEJmgwF@eA^Yl740U}zgu@Gaa)LN-Sehg2lL3hP2?|uEY6__Q<=lm%ChUHvTiEsRd%rz_ z16Z2!@``bQcksQf_`PRLS19&2y)$7!tH$k~ZVP(X)-uO|SXP$)^Z4od7 z(3;m2ueII1H+gsOOW56WZFkSJ-CdX4UAkU-X;o`LQPiA?GX|ciHM`D_;ElDOiRoeq zx`!w03u`BZ(cqs`rx1mfS_zG*J<-7Pr^(>?Py%>fXyAE~f#*qE2hVqClBQ#rfpzZO zdG>s@eY&HZQg2#c{{ND9a&N*;Ug{H|mVeB4@?Bfs$y~3UKv}<&>K&fj+~m}O@#6<)=cY~^!L4Dq`wsVp%^p8Cb!=Cu5E7qGhQvQ5K;rdw zrf)DvOywYP=`B;PaX^(?+R^Iyqgq=4-GLrERn3H1Iu%8IC{O{Xn$$*=^C623W>lM1 zwv2hrw-z_x$XbF+1`f35niQNJpFJ@8L>Y9Y)`k-hA2)_z=Z`v3Z(Ia$lp5?}E(3`R z3ne|A_Y4f*n@V93q9UYSJwNAGQI24MQ@X5E&S3dxlh5|^31|CjcD8S|vwceLY)?Fj z7PWVGyNXUNg^16VCDFGWpPmd_Jx;8?wlN1lkCos7Z8hit)h`MC(g?gh?P_C(l<9gU zIDRRo21_jgvW?Hcyq#mw;8^LH{2>F-k49rpp7Ns=ypkJKHv;3sS-2<(uWqn40qhTy z!U735=rLqG9BMP0QfO7NpTWOhsVo}99dB-&f_5AE`UQF<190d?2PPla_6y1updYP4 z>UUUhU8#t49GwzyPA`I_Mi#2g7Hhr%JXo)l3ccoZCgL~mo^*6j2HOwc2}Qwxkpx93 z^)B6)xssAe*TGnsJV&VhXyQ;q0x@K%*$+#g><<`a|A|rdi9BV$8Tt(GHXYB^pSc?5nh~n;n z|746Pv-`2qA&7l4U#JuI3(v^<@}NTt+lugL0A=K{she2b#&nYL2kir~v?1rP22;iQ zh|q7&!-0>`s}ME~!B1wErK@A?A$Je0wsdDJ5{4j~S==xN(|F zp3|H`Jiu|9!E^|HsYr*If^Qy5w4f{)34V>8#_*+lOywH>WRw{cio60@R2ajpgIE~_So)+MLX4c{aUpHfk6(2;SnlM42YHi19zi)_y^yrBk}aMa~WJhQ$e;)ptD4h+F~%jqCU zQ(@Jugv_K^ZK*+p9Oo(WZ4j04b6XPxS6MUk^57~@<4nubCKeu_EEYx+#KOOrSol{H z3ukg-;nKuWDKZC9OEFbWeNi3Mmyg|05{EJMxAkWA91;iE`D&2k{-$Cv?*MVu0I@F_ z5Z5ID;tK|dFB%{kIY69v9n$HKOY>8PlRYtjKW&KXLReIPV_V-Y5|obb6m0(Ty;61d zIi)uZ9w4~2Vg=H2!{nK)E$Wtpl!zugqhlrX9IGxaRKeC(B^d(ACS-%mD}j~^Q&W^x z8jP4p7zcSar&Nb3d5pdaxiGSKY_GD1N(FOAfoq%34=+Jh4kade))4Q_1k*LmzbgQ` zc#Q-m2RSjVbHrFZB#;f-YF;IqbJtZhXuU=IbfcsF$#nEx33T+3SHPbodDvgePxm7A zW4WEDqYmh361C_sS9{YDy985qRw_5IH_qzg5KwvVRKeL@X@YUmk-Cpw>+F?^0-6an zP*^oY{zh{F{hphx^-gKIHSY~NX4XoHx9gM4K9kUH8fS07uGL@?hiQuhWtpV((K^4m zE+=-pVt%b}a(>`AK_Q(Y9#;U>0*Y`v`JCa>fn;2|H363%VYu`yhD+!3xb)<}li>#u zM*_7Br>8j192EV6ja@#vh@pKnc;ibWqW9pOT#+%jp37W~lE6{}-4wqiO>Mjr_Z0SDwW`R|1B?2C$A`}_eD1TM~`~)#%GxEu80Xd27Bj}_; zi?2rjdT=A$A>2hnxThr}+_40Ndz>NMc0;&l9YC>!(q2UM}0%BEqCo-8l z8W^*Ca*=+i{eNM)Xphf2B395h;0~Z^3ldvFGjFvv#EGWOJpG~_>+a*zPau!1PkyvC z)m&U{As)<8j~qTWTiPAFRCo9Av3H%l@CXT^!6!RBD_U6xEk;k4%jHuSX%oU$A7vZ( zW=EwSSOOA+VI!mPz@h+X$l&`Q>)y4v6p4C(EAdIva@GHf3|ADrFqO{@4jhx}1DN=* zyisUEuL)`!kluO&iYpm&;1vh_m1(h3aiY<8D0>h}yW2GvO&6yNv`JaBW=!ibvoQo} zf4Lg5`7z1>2MvLUYa37)qbE6tA#wiw6M-*LB9bt~cKGyd3Yk^bx0Z zRqxO8(Kp>t@3o4@pjKT8DW*~VZ0tWH#hjeE%g(Bnd{(Cu&g#i_R^xV7FUXzMiMJx` z1H&8fOBC|t0zdz0^qCH{`~@(|33s1{L>Bnf4bX$-#slGkEaib;Omb`fNsS`SJ2Q$!TFE~ z(P7GvE1xpm%8$L-8k|I%3tnijX%{!+#S3m(P~(uWDZEq-oFi>E{u?W4{ML;!8jS+G zN3$iws`ez{K~y+xf&L$e$m5mwsRwr^GNkg+Yl;CmM#9N7Py)wH4T^Zp4rC|;>TJ5H z@x=nYB14PJW>zFh38h$4qnYhKS5cCt1Vt`)oI-dHG$=Ocm*rT^4;!mlOJ+6a5?IY~ zV>P!KtGPSRYW6({1scFflp8t0fJhB5QlUj9c|wc_?ka82r%UTfq6$p?AMqX@Vn7Fa z8DrF}vb@S1%A-WYSKO%xf$Psy6eXWY%^U=xql_zwBxK`4tfOj^1z2T*pJp?{M@=Tc z=V=`rM!x{rf^{HtBE8`=OOd9bU^yB7VW-)bM!f403}4%$`eX+Qv^)l{gs4N4Y(xyNtv${ec8Z z7T;!3stVZ+1w1QQ#C(ZQzVL`~Ulep@IMH+RY{O*?2!%TG^zL^r68z#f11hc_sm-yR z7ET8wz4A}^mY#viVSq&}S(rE(>4d^{KE;PZ*eV*PH$dfz26rXTEbKj&CZed2;jqWZ zZ~JKlf@UDBJu!osM5he{0{uSeB%|EakYuQPcjKep1`WM~OQn!oUs0hPsY083F| zSY{|s=yf`48|BG*XSGq?DBrfXd_W2sk)th@Laf=kLSkKcIQWb)YB?&bk4Co~E?5q8 zs>!ywREujW|Bhw43K)je7 zZ=fLxmLiO0pg}-vULUw}YDz?uTVISLDv}gx2M*wxHHn>VtQz}*Rh&v6X@|B7^ z(VYN{f`V`QdeQzBt**2RQXmFCZ{Fh=OoGJaWk}pddr@d{@S*wY`dxA&9rP}j)~oH> z`8Mlrn}c}A9xP~u6Ls|WI-|drCez>D3H0}Dqrc}E{k=X%f0tf#RL>)_Hblt@MICC6 zpzR>bHQa9uf~_~IZ7un56zUHU^fw`O;XG@(B@zlXy3~TQaCp!n@Go=2VHMEmXPS~T z`v-wy|73-qH<-R68K$pHfawnyOn=Z|`o=s=Kf|jGa_S*76_gAjKo&t@XohzemR$b% z42c)Wh+9_K_aN9F;9SNHC9~_iG}(8S2}z~iTwPxqBIrd0j=+r2FFGpV2sM)rr=*@} zCtNzN8i#WuB(90@@&{_XSP68jwx+)^7U0>>G)%X2-@{$Kl#>6TI?7)%eEA>A`10xm ze0jOy%a0nqyg7$21C*KG+Y4-qs^hw9D)1I4_~tOWx{3P4J{2u zrw|s9Pp=#VDgwd0bRp1SWHMHJq}^J??XNT=z4c!(f!80sPU?=|yGu27jhR~NGxt6Q znBw?G?jrVg|3-U;0wgA^rsc*Cjnc@GY6k^{l&!Wbk8@e41Vn>(XnO#8Th3Oe=6Vd> z;E!U%B8=f~*Ve8*Xp~~7#wN1#er-P{&40ENdK$uHJtVX$U^RW3iZzxG=hqNvy|&Dt zgbVu7imslIFwm1>aH}&0k#VdKq@H_p49Yb{tDG{$GS%}XUI`u4@mu{RyW(%*)BeJB z?;RVs1MyE6V3n|Vlq36-9f;c!c9K{ZOkWT5fyU|PO221Z>7Hb+^zH<%bdPbR|Hrt} z@8`MFbx26My$bD}N)J@k+IisEf~ylWaY!fEJUp{dtj?h+G=uEU3}pUH30FLUU&x&z z`Yth)bzYtqS(b_68eS1gMZAqjBV*7I)`MJmfTh%iuq2(aBf9T~ME3KOqN)NIg+Xy8 zG;X|Qx&X1q>5;4oA_pAecVR7V5XTE)hgDIzg#2G#TZLKWe&-q-eFO+twX8~cM(xfs zcvc#QrK@d|PTFBLPRiL*VnL_VxIPGQUX1M7Rl@zzd-0!rdv-Xp=Z_6l{x}&`K9GPa ze`=`mK|_`I=TPO+-UBr@6|*B0;R4&x6e_w44N(&IakB#F1_I}e z(h)cExThS?OlgqdG9{L?yCAzJn)q8Wh774Nkr06}K=1t`L9*;tWfY7NzsfTFhpRA4 z(I-~kY9f;gB`xAi(Bc;a$rqRr;GOPE>urFe zZCi$@ngtn}0T%FxK(X3Nar-(Oco5^LziIntMmHZz#+|=Tz@2|E-1(H@&WH23(+2J! zgpG^uz~gXT9Ije2N4C%qlk*+)9GGFY;;0pofIH}OI5c!?4cET0i|d$b5Ok6|Wk8Bl z#CoBhOR+>eMy2X$_6tNH&SR{n-ty2i>dHKpK`uA9(`#nWf%(ajg1FwLwJ@l(Sy}_P z)a-Hy@u9;$lto7u*eDJi8+7U`I)5SiaCC{R11Q#qddQ_C>H)=A8i^+x=iADooOoOC zb_soPR@4tR2VFwH; zE&h}~*M>|Z(fZ*#7ZZzL_1KE4{IUipo-Ua0QQO-CMMl_vge6M_n!2q6S(oPk(i^{a zkihSa-<(0cDZoTWM%InRbLi^_?1G=c;fzmvpq3;uASAmn;Q&ev#3MWLa52OaF#mb} zq7qJa;9@tG3TU6^#bMs#awh(vb!R3ic`x5vVgnoSub|6Q+Qe+kOwws z@ABA{B44~KLJU^d{q9QHy-sd@(m3JglR4oR6FA{F8Yg_1al*gJal%Vqn1n|NjL#_< z3>3s6g01+jJP{Wt1Kt}IP{^|neB-5$D^9^Ty+Vv+D=JJ)+ZB1*&kq8tko(*+*7TQV ze`ja-z<+?iNos@nAU?TC3#f0iGklbt;os-Z@WclY#rB=m^5xhtVG7)zQoYc{#c*qe zv>l?**uVXP3*?GveR2Da-A_dUJ%Y!gXjdxwHoo&326&+B7qlghX9CtZ&X1BKXYR+| zQ?ef71^h1Hi%hhoJjb(s;P9HqLN}gs0EUu({lenMO@M?V!vD zxyu7yP)d&{ZlC-wM%dqwOxO=gAndXc_HHBWf6Wv2b71e9nwmjsY3Wx$*i4+*8eLy4 zq79fSJ8?eqV*Wuq6WnEdKEl5SJ;qriWFi`#xRMg$vaztgi1>_Q(j$^F>5&PT^hCp? zCmAOF?>r{G8kpoIeO^RBAt6Oal)x8(`9^ z_TP6yB_q1BQY%$x`F@+;OXvliMBRg~MJKqNi5x!MDC^CTOe*SOQF08B7>|0qaUyiq zdjedde(x8vNukol!XqYSwo2rz-JZXSz!_l(?4VE301oC8ix6X7=j~#Fo(@y2`pJQP z%vU_1>u8hq8pL~Nc+CQ`&GoJr>u=R}qx+A;)J zMn%rkx0_jcfs(XI6ENQeW}$DQT}y9AWsAK`y#e@Dm@msako?eteGKmTJKu|U*g2#S z%;3z!k<-^2IXx+voNh=Ur<#$|l9AIx^W^jrkdw0j;szRS=dSEim5r;cf*G(~ZG`@< zx!n+2L|=wIqpv%za?jq3MR^Y0b2OuFzePV7Wqg!sHY_#)UkwvdY&EB_t`;xYyO~~ z=nqQmRo|_#bOlf9d;6Z0t8Koz8S@U(C4=-G$&mh@1W3QsApOGz=||@weUFDU1C))L zkwm6xeGyF{^ICx<&TN#DY-qwG0^W~lGQ-#5;wUZwm-1yc8HVx9L@xU8k(Nxi&=Bud z1XCr?w8xm%RckDjMqE}5>&(!~xINkdKNe@YWYV~1khL`kiu7cNcf=HFTky&HzQ1G& z^$f0lTxlX5G1NJmj5;d`sPh^_ou4t(*^xt?6WbPj<{Fyonv7^OwfOpb+S$dic`B&$8pEAb9S9rm zKWwOMxO6TVm+nl!r8gTc{j%ZG?i?;%dQuW174e~T!vDv<9REwW)9m$jvd>LE+3!m@ z*|*!tzQa!T3Hg)#t$qS#T`yJXr+W<@Z#L>@Yev#BU7~%3q~=U7jUHkt<5-DB!0uKn zFa%_RfgfnQ`ymw}&RggOj_7~lIYXAb>E#=&+vJ5PgUFS?>Wf|${m?r>u9kRFR(7>? zOsBBiX!!KvWPEyQ0zSRp@aY4FPy6%u6mtsRYj8Dm!%wFlCEAOLjdOJl6dkXfaJstF zxPzUrgMC%C;#$e3KTZe!%w);0H@BUX3cE9bFaA`|V z^snHjdmn&*4(E6O1i$;NDa1sMe%P0;fP60AztXi>>f$H3f;D`;^;8N^G7muT)L3ri z)o3zvo1O4Gl27V3oJ+jk+F0a76juPq~7Bco(E1u~NZJ<7nbjMiK~` z)jfS3omF5bAgScB`}W`i6AU`5)dk!PfHHE-!~E5169Z@7!yh`OzWey>n@ID&nU`cS z!5>`$YO5$Ygezhw6bG_Sz)$HsUUuzo>F9xp(zQ=|;uBEki_S_+Yv8J6L!(0w5d20; z;-X3=ZEK~?u>xF2LiAAIy8+HNlw`_%V{oXk#|x{SrT&1EU|84vXdur;zJ1Y>A@RqO zk@&9@kodNjc-cK^NPIev#Lt05KfWs0JXg_#5@{>QNU27Znev^Brx2gRg>+20W2v>4 zTYwGp4Z6)rPD~WM@=g1FX9y+(57NQkH>$!}ReYXuq#N+gAjaT`{h4cn5bTj$ljzx^ zp}22Ns5QS?;Vs)SeC$c>dx0jV2>Z`XwiedeFbyqCc`Gd!fe!E$bn<;T^>iw447xM-r}Fasb) zCi-dg7aV5W+2PWNEAm2-)Np47L^3RGAcq&utT)fvwF^2%JUXiIW)P?J$F+iPyK_8@ zvC`C?xP@r}5I8h_Xi5^;1X#QunEU|MYp(mh;3uza(tCP$w-(lFYol0Ut-9j36^+|x z_WIHsgQj!R%xSD7rC@CC8FuHIY^;MTk&zJCmbkMtF3ELcL=war#$NV6Y_yef>u z3by4Y^UIQcpBW$gUCW<11G;?^e8kJ2#HSZ5e;)s$#nH3owr#ugYGLpN?cXD81}t{T zDR-f-{Gw+rAN7Dtlf`#IhDR_gejv9a*JhCbbT-Ml@wI6eYxBm}&gJ;p zr5#zOHgeb19@426bn~`Pev-|d3#}*9e&s4>RJ>QG*NH0%`yOfNqHrCsKiPPys69l8xD{h>$ z>==`c$MReSgnXZgL|1U-a$i)u70N`<3GaJZmg7``(KA(nJ)FU?DVDr1yt&>$6J{cd zHd#{sPxc?lZYZL-Af8uap+}C(L}kaT?*FmDh6)LcSZ$oIZXgBFi;L2TRNcxNf-62v zZ1~h)xKceTA`Omx35j#kx~79Bk?1cVd1mXZt<~ICxo(WAw$O{Ok`mDzdu95w!%B>L zB2Tu&4s)l|Ek~YjtnOQrS>2-&Slz3P)ji)>-CcQB_u-JjSp`&Uqid~Eckv(kqyT)K zGxb$v7>!oz?D}dHW*r(j(&ELxIQHE~(ay*}9`$R}HxD;!v#mqu{1HFp6O;nm&eGft+4N2 zyVsc=bD-7soyo7+CyC>-0M{8W6f5Y1D|HJv9*@xYhk1~wU0EVDf2yw z8{^U7)Qs9r>R%8zkIH_x4BelX1xZAWs(nj zf5CSn?N+s1(p3q>4@!V#7Mvq?q{I){5S{_XKg!#V4~8Pwt8gq2@0t~wh+{iHV&ET3 z2LFu-;D5V;|4jz|m*v2J;@e@sLo3re(BefJ+uP;_@SY&?Pp|Gi5q9?>5_=Di5` zPVyX$(ZJ51g%VI>VCskqhw^GmO_h42P+t+*^YxjXg#-REJK*u;1D;4Y;P==8|GFLU zEAt2ZOOPv!{XwPYl|B)P!8zvp@j|RvDKaAiz4Sns8)9^}yb=;OjD^t&mwITUlk?U` zj&0ahdYO|Sre7bYT*oEX7-%>~lUWv$qW4mddqMM$ZZA~1*sw`^_6Q=f{s|@%@T^b} zQ^OIDncs`orr;h9rOmD?_5Rrk*xO~AdP0h2kKGK4bd%rZwb{K2oOJ?mR(}RwH;#2X z$iN)A;<6$4Q8K`x49-T`4*pqLlM)R4MKnV*$={m9xq-)lf5}%mx zUGy5RaxCJGyUb|8mx2}c#R^W`an?xuTxs5NsOx?lqLE+?3lOeU3-A8s=g9&r6`#|U z<5I6NF7@5XT`(sXsF=^)opxb?K8+dhZWxR8hF~(wL@F$fz=rY43a1fOeo@ z^+?Z2n0pM#{kQ`UO$ZRUmTx4>4xL}=7nB45RW&$`r=jcsUgAd07J^7~>x4UB4TH8s z^m?j{I3wsb(Mak3#c$27DkFoOV0pbAS*75}K2qPdZO3!&zIgY@@;!*5%`C4Ygtk1N zaAc2X3lK?ryVs6vf$v4t-I-fQx{>$iv9y`x?^xi!e}mcYTAyDoUeMDM7Igk)enFqJ z1+}l%f;yWo=*%M&JYTfctX-|utZ%-WOr=sJGZ>32XDTpjf6$rWs4fJMLlE7i+afBi@K1os2AFz9%_qv=HQE3{(8QhLUI=q z7WGnF)Hm9qo;CQQlm{$7d~-SC(ywAcmkwN-mjd;Hm`Ds1+Ls4ywi0`h0`p3W~-`F1MbPB`HvTSHh-WYn#5tHvNnFO+RIZ_oX&?D{YtlLcKOzN5*_ImUhOsxoP)u zc4>sRo-Q~0_i%f&!Q+L=@c5wwc)ZErvCH7`%Q<+QcpiQ6dU=4jO@i}Dc9{Ya?G_{r zEujE~Tmu(}o{>(Mgr+59ArddWgR>XFu@^mB*X#Av)r}$7v>@((ER*o=8@=o;y44Mt zTv9@bz6g4}YFf#Saah$;abmI7RjE9=#S->1R3UFcF*9sJnyS~@(~ItR3Gg;s|o9n|t4k@934)D1Al1Er;F{wk)OJG0X?Hg@hD z^0yJtn9xY_cHS9@&Sb1b?Oq6&1KsXMGgv;YTU0}nK(HYfNWX5+p3%(`VlWLq z_>r8R{~HG9S0=;xRS9r@r@{FqgY$3Z;rv=}nh{fdQjVIx+7 z0c*W<;OOB);0NV$sl7hm#%&!;bgZtdHfo)rA%Fd@VrXWXR zW8k@tyiSOfZohTv=oHUkFDg&=4j>`&8OXnF;Qb%T;C*!hc;9Q_y~V)$J9+Rv1!%m6 zJa&3Da63C7*(#l`HRa-1;IcN(LZXd&I7)TJQMnfU*yK||_i(>=aFYs>T9aiYY`1Mt zcV@IR*lUwF{knurf6z8PW1D_^Zqu9CjA??h730>MF$ye2CbFWKu{vjA&_eK^}_}^i@vd zd9>3I{ScxLET|@mE+d>V~7fXZDM%$<&0fI!uAP zXZ#wO&~?als{}gwewET;Og$;edqoU+hl`Ga$8J9XG4h zdK=vt*6Ye`$eSVLl*coD(Lo!NT1*^ki90mjR&A{7^$4yhVrF5Gh)^v0wo!zKQNRSb zf{QwQQ*!P~U)H{B3V{I#@Y16)WAzZPzh}7qE6KS2s|mRNw}$I;hU>qd!}S43^lQkz zVjBHJTla4yulu(W*8R`6?$fsJ_g==jDZb)$zu(sV_T+WHGhyAIvvs#@-5 zTL~JBHJ7L6SlSJ0t1ckD9v2hE_b{`WeH9mBG3z&(`Cl56(c_JO$Tt3a$s7NJgpEJw zOtW8ZIZ6-A`yLs$d zBxDTqgo1&VP_|XXy=8~4!=sXp8C zjagv*S|#*%f4@)+1<`DF(J_E#_aMZq>k8r+KM+?Uj|(NVwXg6*ssr?TD%+;pd^gcZ zj&MI?Q2#y9RGIOc{B=r?gaJ&D! z?f$E{r=Q}(Sviy+bO+PeY z)2D3H@3u{UCFhFb@|T|6XVc;ty>dIhf;n;zGLU_9GRVFq0c0x%vJV-^zB&)GNduXQ zA>0Ic_*iJ*L*E0<3FQgm_t-j{+6TRj&3^MFBRtFHJ%fU9v6ZoYhge^0P#R8#(qj^! zbittXaf8xB^H4fa1f?7kxuS4Fr;a{w%0thA=wSw;$0vhmGyz1G)W0S-) zpu5boy9l-Ac?R;B|JZ7v{8D_2(D&Jw;xNn`Ezc{|gwBY?qAfIFFK@GF|=%rG>?+=8EE$VNi+4(;2cEeVlfA+V6nq72l97)N08 z&}Pa+ItOs9{I9OPI#r}=YWKZ=-b28Bjhw#CwF^80&v3ggsqVd$6mN2k0Sj6UXgJQ+ zfLCb&8FF*l(_N;;j}zm^CdMbH=5C(4b^NyJ!$;?iP8~XY+tl2_>6xeQGTTxdFk^u4 z8eXA|?g;NOMtC(sJjhE= zb2sM_7WDhJpxw5h%GFv>HDN*TwFT|714{!>uo`cS8GAFgav)j7W710 z(9+df(CLH)eb^RsgDvRH)ml(JVL^Xk3%bb`w0yM|bT(l@_u7KSZ9$EzwV;)R1%1L6 zG-(TJUabYS5*GA7Z9%u#f>tkoLB)BaPfu9Tr))t_u?1b5lT;_(gUdI^(eDeFx>uKK zNGU@Qti6anCfd#ix++>5YDJ{WeDfF3KxY>px!iyOg?!>nZQ(4!>4fnv^nU^B<2j?V5req?fdc51gO7`rEr9zL?Wwzg2-!G1*( zM-Lx6R(i_e>6u-nqtmzCI_o|kIDYV8>G1K{siX2mX~fT3V`xya4m-6SL)Vr|$7iMw z&md5${~gC`xo39_T~{tqubsJtp(rL2bUkrBR)X7dcb9ITp1rl_Q%{ioN?~F#DmzG zifwY_?hE?HnV~t)Xmh=})JFWXy}kf5v?jLG#Np#Jvm-lUSJFU|)hsRH1Zs;wterb= zueO^WX<&A?`8Vu6MHofS&Kg_$)$Xhd2dulURk zfV@y1hzkp|+CUzq|HTEP6tOfb7BsQIHbTs=TtO`nUQw(kJvD0}oA%iV@x`2V`Ll?| zA@)fR_aSTmHE8_xo#B2legx zm(SOGf~n5u$Yzs#$`=zD^0ptvXp*jQhcV>u%rWFke|w;@-Z=xl3HZZnBSBbmgX|W* z7a)HY3C&}r2|iomXD0V8$@?Qx1&&LPR_hCA>6YcyM|SnA^(oQ;d^3YFq%y-ZG*+5f z*GMxuGRoMXeW@OB7vRMiQQ#|Z;P{&$i%zXl;Veqp(e<+f#6y=KK*%wraQR7gHs9NQ zHnruC;jm9MV8RC{j&IGQpaisz9-rJrG#X`>u+i}&(^~Z@&Le{~$dcFhFz>@P(UU3z zz**}lbTs^mNHiko=u5Q@>;!|=_IDt3vN~ju(dnx3bK$2t7nyNe^Tcs_9>TG=`n&lnl4#D9LjhLS+HOj`cAk6nM)rl z^=$rxyGUx~IX+aL(7vV+WsvgfB4MW+G=NP_wE~8 z?bA2G5x7!b+<-5wzR)QnstEivtPDdMW@<+Q1!eF?MU8YbP%e$!Iy-v=zQ{H9-C#~b zYiS8>o6!&zZuQ`6!r3gMX&3v(`c|m8(;k8fn9-?C$ibDFnle{wo*QxEiD>{n5z#NG zKk)Raa&F+bY}0d6?XehbkZw#!IP^IyAJZRk$aL?@1~G6GViGdEv#k*T+8180pAzvC@23!qDfqS{eFHoJ5AcF$^sXCga}d-f)~= z`~=T+dfs^;3_d~)`P;ejzRQ^13%i+Jk8;-^O4#Jfe%^2Lt+vS*^Sxd^obF8)`EZJ% z{*r_Rz19kXf5R5^Rpg^fS z4^{D~i~q1q<;v=s_BaTy%C%Ftzlsk-_CQw+^8Q&0P{CK3h?A?r))R&zKU`^0UO$ke z8E{|lc>pqK|IBIR+QI0cce)hnqCyV{U^7qwW81|TEC8nIEiNI7;Dq%epaatbSb)h! z>xdR_z2<+dnCTYK78kpT%|Q8NdA%PkKHU~nbq4rl?yo8d zf4fR*Y%M_OLW_p*SVTb_>f&dx)lN*#qvOve}nJqG+P z4;8=924hGCbowQ%ad=Bu_keL=)8W<_elmv1W>K{vAOa#wbj5)67zG8TR5atl?)<7V z`|Wu*5=K~a%yRb*uc zdD!qV_@V8!p1WIXtwx&cO7+hA3bL4ytSVW|XxrszMj;2o(+@2#`XvLGEBV9mH%WD^ zxUjC3RH7wKHZ^f zxBe7(rfk9VCx_nYDpZ&GWxOg@jha>?r%IB5puQw?i;@Zyq?QB55AS%TqC9{}_mzqx zTKxlLrXLjh@!1`L;i)T@H>m_6(n1+D-N6~j1zCY2T(DcHT~w)XPRQqIpLTs2P+3ln zRjJ5~1b$c*kyzZ#k@60wGScOX)jburUMM;qqRPIBuKvp3>Dtwgub7nmxI&Y?Vfuex?( zAAGS+)B{^1Xa_zzXxGEd`X-uS^3tKZaiVM`S^;EC$j|n&px^68A-x81{56{vRmhiY z8o@=k@|Hi%uuP>p_wF3PNV6EULL!B!S`8Y);gQ|n5vUdhxBEC zNxZ0};A{wDo0md@J=jzEg7fk$dU9> z*@zq5JA3I|FK7^fc(oLVIov?;8@#+g6`S63%K8fH54|G^Hr?uCQk`Q#Z0Z<=d+uv~sbSWB{L&U}TvPf`(q+JHNW!*QgAN?7o3st= zxtl@J+tUUxL>Hk_`9|yZ9EhFZRL_!j)6BxmUG(PRp7Vh^0VL7!Io>dYO*#)rL51f^ z9d}uQo1A0+1}Moh)tTKiA*3}_sdH8$)e_ZBV(hrPKL;^Bl9{M%16&v z!GZmb{1z$uIIufGF99>~@)nXBgXY-H3nq8gTNF&0x5cqbyTq^~jJm|kA4ik|GDDU8 z3x>1xCJq$-M!9O#8%J&qNIy^~9AUgrb(AnvEnZPTY$*7=1*_#MO+`oMwXkRKJd!_< zHIrRwS(fDvJ%Xg}p|G@A#3as6vP2rlu|Pp}&7G0r1%9!!(_9m1GxXAf)56h+HINns zmvGahT#x1OReWId@^KOy%>4+B7nrJ<8(>Bh{9sqE8WI^jL^v=xM8l=!kK(cnQmGVd z&7-(2)lm{^C|V$HsL+~CBb3O*bs5T(geIJ1km^cjdE;?2bwW9Nw>nW8!5LR&BIk8I z!Yc^HK>^w(SU{sRO{sD?`_zysQa5ynG?fFegv|{Mak1r|>V~e`$pHmDsxXk{L|?h5 zQu>du%*)h0%p1>^2B+8Si=%sc=n-A|NB67ydc4}Lf^;byDk8E92Yl0>gYSx!Ie`8}puzB5_1yeC1m z{N2}htN1Jb2%qkS{kuGuS1mt_Z{5v5&OqZyGVe|}sJ9b>(fA*%@zoch2!++eDi!Tv zEzt7XfIRFqIA@;EJQe3V&%JBryS_^YE{6=DROm|fuyftZ?e?g#fpo>c??!>YS!w$mzZj8Af-Zjprs}hfR zmZF@mkP^Ugx|(siKkDXmF4^&=U`RBmM6Y#mr)ShqVD$R~wB75nULnN_xIfvEs>S1DaiG?R;@vcxxU=LbhU2@{CMXpW$G{|IC;%J2@Gtm|R{A6&KArvF7GwPqG*%oj6i8a=NFURF>@& zkKN4|3ZUWnMBkLi?jq0&@Ew$&HoH&s2@rE&<3OpWL~ba_ntUK^@7IGLRZyjFZgr;e3es43Jd=@6{(80 zIyo{uEm0Qi=yhG<2Mdek)4~V!4ZTo;W#HPn*nO(KvyaO>{hbqzCE8iq$ zlUuYG+_ddO;KN~+>o^i?as~1(j_5B9D@mcU<^qSj$Vj@Qvb;(iPfe}U$)etK_lptr zj|912b149BshU#C)pn3o8l?cFCsrzZa5lK2)FiNo(fM8M4(D5pzJu&YnooU#>_|EA zrN_nUuD*ScS#Q}PFvcI^_3yvCY8L<%XJ~$kLvPZcEQEsI$0dneYUJe7i8q2nIz3|m z4jH|6E3G0uqQ2BX?+MI1I9vbRnD=M-UZihJ#g8+}ikNqlvQenu8t$9E0C<2kv9(nL z>5=4{XyzRhT+?6W%^~=(R24$IVnoV7jlRAd-v>V$#I01|ji|elypLL}c`S!O!_w~2-tYKT5-Oz!LWUd-ga_iKF(QSOu&SeizuI#`ERz2uD# zO*xFq42I;|G#BMR179Osuykp!J9ic)QF}F%o<{*-l&vdZbNRsRK>|eqG82t5GHKwN zhP~QCIF%Yr18EAU*X6<*sG`0&CgQhJIEm?`LK{fxNsS5)T+r${?UBrmySq0@X2;qr zO_z!6qK1-q=Al}tcU#+wC^YiWEdU2w@M&H+VBph`_seGwl*8KBURexi1Iicn4owa< z+srR?rv)dvo+GMe88r3%8iEgC+{m=~(|zTa}-V$2>^E7z%@Yl?QWgV1==-kG5X06QQDss)Fqknv%L* zs`0YLWpEhkIMFC-zsV@zoA?3hPS3J4^o@lggXi=bvG^Qh1O#GPoaawLC+mL%UFrOG z22zj~whQa2Rt5r7H?LGqN>BqFX@LnW3zF=+2$-;|>F_Bwo5*e*$0Kj{Jfc2Zllt6f zTejq$=O`6!KJy>0J}r%t60S^wHW3(HT(~C-4EV^NwUx-ZOx$2cIq|1QXwX~Ob zKP!qofYBNRH*X<{o0TayC`s&nHcu zJQbT5hzJ?nVpMo4%VJ7s#{{Jckvm329R_7f*>FaDoKG`6+8fXFXAn)^?<8Eo$ki$O z8(_I#qJ@EE3h8H7Sg7Mq4;U6DFjzK*`I-E4JpiHneF?r(B(1 zC{MFO9o69{g9|EST`yxy^~{`>?@cLvzuT0eHZD^TgM-V-`}j96R+N|H_=e7;@{zTSL%Oc~Dd(*V zzfkIp!Z@KU9ii<_8*5F|}^^`Wy}AM!qLyFN15t|uOzE0|0g(c2=?tRe^Z zi!w{TfhwwbG!^$MNB5%c|0R*v0bZNi<@*TBP3-c={vn=`WQp(O8Ql8-{L{`yj-LBf zee80EJ7}TML}o5M*`#IC!J|$FBLxiL1-FdjAylw0NW)UWPtW2zWs*$Z*oJi-ip>3} zL(YI<;S943Iis*?`o$c+Tm7CEEoc+8>-JoC^&>429c*H_x$sK#f)`YjtCw$gY4m1O zL(1!no*>Mq*tCO0isC!dtNkkW{biw@M%NERt^Egw|vxOo#Spd;Uutsv%$@vWn zp^XPQzexyfB0-AXXj1HuNwH_-rP#Tz_EM}%zl%aAkRfnR(sSmSaUw~Gi}tAWmtc4* zRn$2}P@Sgb5-YyMgc0`W>;q784a!>^{-m%{d@0Lx;f%{$tqbNmv>BvnfyLzAyM7(9 z2=l%zk4hzFzWTMxHlU-zzq=X4^~Fy4K%LiwPnnkwa95eJC9qIN= zX_|gNhm>1?G)%!RF2l#f}hLuOo=-ccFh?pISdwWlsp zW;j$;+L68k#v>XRwP}@Fl5I?9$@ zT@1LWRv@_UKV4c0QL#uPF~O|-WIqNP34Mu%ca-*{)n(8xx=i+BUBwZsYp5h{`ACxi z*|D)u7`5Omx2m;d{Q(M%ptKZu1AN?5m65QYCkqEp>P$9>;&NiP+vNAW+zXGBQD#q% zgD*R`qQ2TKCrEPoZQH@>GwRS{#9J%T z@5^8)k`yStzX?33T*q+j2AZgYe+jT^Z8$6?_0zRBCYi&XAlc>G3zU&Tcs@@%i0z1pFtY{IJ&;9U1Y~WR*2T|QqwwKn%6oG z4YBG4`4yVo3=+)N1^N~W(uSl;e?pVZ($3a4*uTcr2ST#1WV$$5G6XmxgRg&mNck%}4J(2(POb zAs6Xi7ebFy#Y16Zcs0mKVo8QV_FE?-?MKR1RpS#AqkH!3jXPrUJ*6PLkn2)VI=HsT z+`j0Za*lGOU0-3hwXKpu1fre(#GhvJk(0!;R8(Q<7Kn2o3|kiDIN81lLtY z7~bQMRfq|z{!+tmf^@4S*mX8VB)IkCiD;13L%5I6t6o$ZpdyHPSgCIK&RHS#PTdCV zFk926qX?KT)lcKP&cz`cg(?Qyl%fZt6k*^d7|B+sRQfO-a8O(>!Ibg6b9f7vNVvkt z;U_(D&-H)^3(u0xic6I^3M(q)m=+kS2eZHQLF*POt&!5Tt27BN9E#9S`NG7PnSTAu zZvEQzM*84a=6fS4tLxdNXuPm5lH-re$~oe@B56W7x;RN!U|&(Fw>u)IXDGB=fdm#VRQIBgE>CC`gssIe|TC57`kbUczb@WMJ zou_M+Idh|TSJH8GZ|C=IF!TYI(Becv`1`f=kc&+q0dJ?>n5LBb|OgP~h(lRv=sqFTUBE|yHcbMXfg7W7m* z(1W(1SLL97VjGemjd*dhFe@Z9FM56*H-JM6p|gzky>Eh!KH*H}23I@kx(uhA+%htn z;>pN*)#AALSw(0yI*-%pt|d2xX>Deisr(gfxmF3de{%=^FRnfDIFo~P`>T!e`irSE-ob#8Fa znmSi<_oags(nbm2^PW|p`DzQka(|ONGX|x(4ZfI1J8MRIW=HX)=OOND)!ilcVRHJ| z?DWjUY{|ait*bMokv*f=8_~(#qXNp(N%ZrtcC^pq^vn!ySC#8Wcb5)M9hlY6BVyu? z%JaM6YGWG>P=MSirs$$qwT|rHReCi3f77ngjrbRzZp8l&PCs>OS7~}?S83+(to(QS zSZU_?!Gpox2d42m{4C#>Zl0RGeQFACjL#td|L{>viU#mV7t)5NXwhFor&!{rU3RM0 zn|xFD9xkChHgv4Q=})QQ>Bihgr==HPeB zIC_igPIMGHcKpx?{xg2tE&R`+=^6RY_=yoPgG$8#ekZI;=l$%gukuP8EErv&RBY4W zl(012Osv7w_Wf*^d>l2uE|mbc=DJHyI#SiXSVu}SY?)H^T(#a1W|J9)LIBM*_ye$M z26+QEHF{D{9h?Flu)Jk`o`QiM{?f!OPUF_`+oor3*;P6|Gkthw*U-`FTW+1*eE?^& zd*bNfW5?v!@#y$5JUelC{2+jbPv>D|;TD%AT+hMo0|*zu^9^6v$d!G6O@ zADcQlyZiW&$?@5#-IG|%?37*(aJ~(z6-z;|0pQE3;{c*IS74aLB{%>oSK{jCgD&Kq z>WWvBAyYl*!=okByxaxP8c!G3L63W##pAx2u92BCs=Vv@9;%mX<*Rq|^Uc*xeLKoXNP?$CCAf5g$nyvEuhaT z7(O$r`oR4N(h>&|`efSGG&IOqv=+UBxkQbq!Wp&E>f} zEkCFXu-#ZQ;?&W=KLHfF5+nmmR_%OjP)E6+g}8APd*0LC##cGC^;kBaXT|uHd`MKn)vma@6f$sb^q}7dxme4>8KZF&(9mu;deY? z)4M|!Zd}@(d8UCzHlrC-a8*RZa+}}&VO7Y&Mf84QTtnu6Ypu8;I3kr$I`=cSmatd$@78uHJuh<8FbN{s}mm8*&P%K5famgo4H7 zbS%-8%&0wRN#4rJ%(xMdOUrkCB9|}Z-JHl!-H_o?-p<@2$x@|N8d?kKJmeAbJyIYt zQ+c$H&C@H9rgZ7SLZBc_XHmV*q|HwxOPilckTz#b+SES_FnTomZCP|<4*J^{s@H);!Mp$|^!wt9Pwu?f}Q zrAQSBcD{;ymD-&PjrBzYnQ*gL6)LgFkBlkk__{I$Frk&e+MPLkr7Np^7G!4wX4gu; zd;a`}#CZtLFXSL<92_JokcK)0Opv9e`kfFKxGsB+w>jv&*o@Oa&(lDCxE`DB0-u4> z$M!mJyU0F*(^_9!z~QvdCY;?T^As zt~?mLbjdd=^p=IQ&3rAfChA}?mrE%o08vweZNMuSPh~cK?#__JP*JyQWdxCRn0M`w zYb-!HBZ75e4gpuf$kOnIg_GM!1KUrHUAw1qvBbZL*7$oL|3326{rpN0eETW3<4$+U zJl^3T7>M++%LC1I{*%%I$6NNT#t~YaTp#6T`KP6 zV*bzp&emmbuA#tV)L$w->Xt!}=nhro?d5izqIui%N4XjPTf4PFc;*NJ_>zY9Ew~q|bKY6)bs6>l#L_bBNYK(E z|FjQYu-1y<3aN&QJiQ#JP5Pne4fI0|#5sYlGD-Zhk;E@2lf+vRNaA@$65nSe@hf?f zcnMm#PnxRkKTY3vr|b1!1O%q(U*$g}1f7b{hW7()g>Yd1B!Q!xfjdSUM0YqR$EJg1_Z&08{7b4ygAmt> z-Xyd}NJbHGl}5REJ^eXTs@w=TQim4>4A|yx*BXK%oip`SeKG!~6vYm~Qnw&$71g`M zt@0iBSftWzW~hZNwPc#SP`l z8uBcf=q);;ZiIMl_TK0kjYXu}9sAoJ*6dA-B4+7#m~qS#{fhqQWZB7KGsvQ9)1nvL zcMdEkyTEcPV#kn>185-KL7`tWZu+)lZu*-E-1J4pO@GL^>09&M^qyeCkY@!J7f6%3 z7<*2}tVuX3bY6Am^Vcf8IS9If|9Ixyk%)Q*`@OOm`WY25O6RE;p1|KL6?YDxC+oi! z7#-K5OahK~T#3Abd~PloniAjcwAR}TexcG56ayN8L`*Lp$14x&0g_3v$v(3<&ZH`s z&Eh3`G)ovIGe_XAv;B@?B(PB`K8}CnM=Ep(!PQ@Mx_YlRc_D$N+W>5HJ|4UoQm0hG zk2y|YI7}aey#r^ci_BkR;Rog(mb#oUbN+E*9Gj^~_bJt5(`jsi8rt;{N9d!VVYh35 zWLq>oOptGXL&l>@91NlM$?cwDkrR^<^L=?&O{86=?K8W_x9=Ft3i|dK)x{JFScyNK z;q!kRUwTI}UwT&pU-~iQORqG(^xJv9^d9gfpPK@DjpQ_#fGS&rG6cEYnT(4WRArOf zl!3o&d_C$TlB$mtVZSg6>RGBUm>M-C3@)%MLt>w^NJk?SKJneexkxKDXl0A&&PJA~ z*J8;Gf_O;Q#z1GRh%PniZMy=Gj0CwwG)+IVV%nT>+CD#dj%gsX;-~cIsefApm z{)K=dg_DRjDxDAGj&F`Fx=^9zMNxB=XEQ?u9;o-|z>jv8-2cMxO!eFmXo?XQu(ZRu zOMwF;k_D-`$(|_&--Q})J^bX1fJQ1Cgw8t9VZOF@zUES^g{L#Zo8yVsAR%9xh*Q$6 zRGV;_A&U$g3iGWa=-fQ5w%Iix=?gviUVcxG9d`VdoRakvUYfMn(h+G36tiRgQpk+q z^aJMr%vHO}X;g|?@rxm4et4D!Oxm$SPCK>v!y+~KapY9JxY9N?MNDMzv zlv0X|bm*RizqaPESLCa9^M~Pae@rL5I*EZ6$BA@}9%+BnBd!UlUBX3rWkU?R_&QTD z6{A;n`ky2W9kSE^FVkM$9frPdv(x{;nBJdtGrbNpc?Kn6EjUT#%v=bLU(>BXjg(_(!bb<~ zL+B{Q7gRdr5ZqGaNyh5!-pr_mi7km2e!aS?c)c7V?%BA4$m1MCV`HT$m!pY<&qGrsen;pDyfjP212S_f%COuRx*!hA z-K&dBcKA%qfo~Q?YP@~Y1zl(j`0d6ow$L4KZ4_`r=P5$Ksrk|B`fZ}VG>7v0+MG1r zE4&Jh@sQ@C7T{MZ(@Ud-!RVnXQmDpoJxYT?>@!GbMot9w+~Sopopoq&fGwPj+_=MI zSH!=tj*Cf~EcuYO2j$W+xPv9+$M;>Nn~_(^{nQeAw{J}D=0ygr-)L?JezO*MRd-C! zET1?n^47Jr)v0#7)mC}x$k3t764Gj=r-GfO?Mr%ZX%h$Lb-LAN7ADmQvS^nubO2O@ zEZhS$cy!K^n=cga2vQ0yW}AtFeU{6m_WFFgzA#4Yt0MWK#zK0>eveEZ5Hj^ik>9te-;s66==wz0CAcJw* ztO_;&Z+3J}r4O3M@{ipbi!;;SiUx>TGfmJi!AcYKA`gbQfV>O&HrTCaw_yOfmWRwT z(T9|lqD*c*>if5erfVF`%<7NAj_^fs<&)_8-*vm`s=ajme@hrT==wj+No43h%W0jP6lum6^gh93R4<#_ zos1#ogqX~Zaz1L5^EobE@3yqQ;UB4Q+qUC5cVE1FWci*mAfM&+IXv`*giQur{|&au zFY>*sq3dt^Ck(vFbsXp4gJ-<=T`WpCI3;9|VqK)Yna-~{ zVQ{(OM!{!@b_8t*_mbTyNN(XB%A-RYvncnOWs;w0Pv+w+sQuJ9l3UGK=@5hw{kkEg zCyEXu*QI+EGQF;32Xz%}vks*P+Fl1*j1}_87qHAVg$8OYcsnM-`gHwJBk|=L0<>H* zQwoPyYAdSf>Tgm+g(Owklgdau1g|z;D2?bd_^XCIR2yQkwt!4S>c#OaNUM_uSUSxT z1b;lnVr$(2PFAr9&6_E5xiR>)O+&Q`fB04K&sw!8WG|t-!aLX_a z)Fi(b@QgM|eyG?@y6ZSgL97Q^uToi-9zy*yX#rAq5uR_#Zk|V(`eeqEkZ~ewf|z$f z@%iGMd(CevzhHk3z~!iQx0000V4dS0#!9!Y!yAw5KC6q8dh34F1L2_)m;z+Q1Bpvw zA#U}$^4r;1#os6DK~DoHyd6WyFFL!B!X<|&hcFG61z@3Qh%V$_sVEPSt!NIY#%7qx z;=h!2Gf(9-RN03A7QpmlLFIHLfG-^;&pA7tCIIQev5Stxutp`+0*TLTZ&^sAcDlN- zpTsu^u6AV!NQgV+Ips z&0IuZgJutBu9PtWF=BviQDB)5hC< z1_TMe&^xZ6?xWDlf#B?+A9%|+PUg~qV>wST=32bWiZJr)lrqUmg}zHDqgZG-Rp1E` z&D{`4M7rMzXD$k05btvy@hHe4zq0s;438q@<2XDdypj(YE)5@4g+??r@NGWixpu7U zDBI>p{GaFdQ*uEbGkEOPoaQtryYEK-zc=)MR5JR1M*{kXySx6>(ElIu=>I_89l;V1 z1{2^iAH~kk!7@@E#dDAla7Rwlxq7?Rq@NMo;$~zkx#9^$P$a;EGi`VkhaGc(=F^=c zZdmjvU#d1({DdTTB4lG@2$dviEFoH>7g!-H6ebtSYk|~A(}b+m-ZCw;S7-yp+c-@) zw9gB=t0acjH48QyJq-eYN{M_Cxa^JN`PCM$X$Yzn3($O*QY2zfv-NElbWK5O4!Bz7 z!&s!Rxlxc|dBQPZoJaRQ4j+@OQ`#?V?7n?m6Ck&%C6T>X9&BEhCNl&Opg@-yKE9-` z#%s?Rfzj-E+BNWHvR^MiDzDP4W#<2+Ndz|M_ROX<0v`a40|Ys;DWeh^^3aJHmyx$) zni{-{Am~AnTNO+q?`eo+7*2W>nNT&>AGX-0AE09&a56nZ9GwEo_Gvg&+F^l{tfBtJ zdoO@a*q1Pj0pSy!-$~DS!~%tLGn^+I3QuZzr2KeFm_OGi2BNX^5$?+&tfX{;P(n{_ zI3qx^4sK{{V%L6ptn%ighdDv5x}fn~lPbaj;4;b|e>i)3Dga2->%%Q%#+i)CpSlZ~ zt|`f&#e79cfubxbI#4Pob4>J=Aw0ye7J)}BkjL{3UcSu&JG55RZ83J|DD3>17SJbB zAQ8<(t6OKDie94y+^TbGZg!6)$_izb!m9T3CI$>eRx=U~8_BfPmbUNEajo21mgZN* zc3K)@T!p`D35rNK*G)lX>(GNngGndyiZM@*7|}_^J$ZW&{cQr_98c=r-;*^lJi^(p z+^am|uWoWC#y^?l8%dUYI};?|U)imdA2-SO&w0uBdMJM5i;R>@F0EW%;LI!vSNjwy%%`EzTUpLqh^7%{K(QpRk+tL)0HLNt@ZbSPqQ<0Xhqkim5uQ8$3aqit1w z$?Cccim9T!NP?o5gr)|UZ*&brdK2)q;0CQ&P(Wx>GW80qxGa`7LLTRkSagVrmsep(WxXBU zLUb?%dH4+b=-i^`<--OzQ)*OPhw#(LE=(goUEX~_epn@{8dRD=OH)R$DN(xRb=iIT z{XleyL{U#n)VKJDqefSL6H}p^_X<+s&O$1TZdeR5(Glb4j2Op~iSfn+V*Ga_#?Klt zem+l(|Bfr1)whXbV)-0>`gf6oc$MXYQu2dj8tqKv zXY|53ezwv1Fh(Avo_m0gu^%*m6YVy<$d9iY30Al#txB-2Bjxn4zwM= zO8-ias9zBF@lt|~AXavz5)ZCHBgf)0KPYdF;!K%eX;C8D)rMP{JGh&MjHg$Pq|E}rX$ierJ%3c(>ECeT;^Fj6l%g|x3B zgtXkO4wBh4VK^5vjA3-7Ni>~3ZgKQKO{mGa) z@>Cad-=oLD0+KM1peHv`NCZybau#C0>xQ%ePg$y`s!dpV?%v|C8#MTw7@wS)yLpNY zK@T6DJ34jf@NH9b2d8JA>ZcK8QVfJ(>aqVYWQ5lma|(wsjk|uAZEYpI*h^J zC5wO%nUkJMo&6{6RsPKXNrJNhZA6~iKOE+R;A>Qyr`OTZ1G00mhWn4Ny;k9-1C&bI z5te`L$m};JM(n!pp@wz$+54Qwzz76gOpzJx6Mp6RL#V3LGUF;#k z%cDa>h**(X8dfQec41NJs)Z}d`fbce2P^WuM+d+K@-3KiN3aN3jVonKF7rLxH7*~xh>z4taD8#=v@Ehy*~2vU;Y@M?tK9Md0rW}I+d*|YKRY=9rU-#vH{Swl1dZHI<1iV^vBXk^i_ZbwpEcK>8y7d#0 z{of3Nqf1753C7+=Tz@3ViVnV~*ek*VeK@mBHl zWHH}{5vCOfaTkTmNPdVShAnS{qNEK;B_fsIdCq&emehT9-=F*Q z{XTw=uRmU%*O{5~p7qSknKNg6z{;(@4`ik82;u410KF`zZi{ur5nAE)>Ci1ayG~yG zB(MIWD`yXr!iCil&$6MVbB4N%49#r9ip@mJnDiHG$?Nr^70<17Ugh(vR4oS?g7j+b z+IL7iAIf~62Km#jk7z4}trNepIX=0^w#xJ#+d30_>{-b@wrFyX&7EiB z&A3AsPy1p>yvc6b8?1CYc_=EPvLuusp|c*n^!Dx+sx>>=`MdT}2kK#1|8{)`BtClb zFYfx<=8T?$dx#M0Q8?VMMsFjBn!H%Sm~LLeRlSA+fAR1#anJ2OI$lSewuMyU7>fqQ zXVq)jUBd?@qw*|Gg=^F;Qs^n8YxtI0?5SJw8OsrVC2DMY!_% zi{($v_DDaTwhG82y3`)CV=W(2>$YP(P=$KEsH58CJJYe-wnQCh{K;qU@H{tBET;d3 zwDXWGiQ=0g%?E(f)FG!yJ@PEYpHQV4?=R|(<3FQ@GkhB_T#tmdE@Ad#FHXcaa5!QT z!^@{#*3g11dXrjPtk{b*iP2JjEcRk_{8thcIoeY*$Bx6yR@x^HwX_w1p>KrvpP@(zeF`|xVzrFv7gBv;Y%jmGcbD+gv1{@G zPgk#)x)nkz`RMZS@6Ncf#(4klF+s+vv$QXw{Ie7O-#;ce!ucw3i~RTZfN6`+)ce;& z`IkI_#O^Vp&WY08x~82tD%xYC#hWpvs|io!m?$ih-^oamUD|@IdeL|;X@(~Xgs4M; z)JpdBM`I>xYL9qF8?Q`F-q^M-5nsAA*DC+89dOG6;jS*R;K@~aL1>N^T5bGk-Nn-r z<0n>BJUKMTO1pYei|_Q!veK2&iGnBnD2cV0e91m4c0)6gcm;W)JPmUhE-C-E-aA>M z{nhMbC_zHW5wcX^#P47VColMhX8M9}SSBv`>buN~-UPlPdBJyj#tXh`p;>h8`0HAs z7vjVx)T57|hsV{a?NVx{9TYD3dWIh0L)QSlFrKFGHZ`rG=Ya5tva?6(;YjZoo{|rZ zH+;#NuI-sV-onddi3_p#TRkam-9tApokI<7tPz!WMw}U9-($}i9vlt|cfLyU&_iCQ z8&aZ=BZWQd!l;hm0{6oI`V&&oHBjs&y5y2w(P^mrm-3-`sqJ}!UFY;4w|^L%(;F15BIe(lxl&pqwNE=1G4 zOcCn&yb+rAYA-(hkxsT@{rt_PWw?G!zj;%yFD-)0%TRmf=b|TQZ9;Boe>XMrBmH!| z`CIJc^l2In#p`k7ZRKJ;eTJ7GHBF6g(R6bWYswOz4d?5ku@=J_-YHIP6_mK7i(OF0 zm;Q*Cmtp?Xo$;gJZQC}nSYgwJJ3n{C;n$;ndlHv9=?k2+3!m}AGQ)BHzjNVnWui(f zaSIVPWM~E4A>QaO-2ojI>ED0VG~-e7Pb$DlQtD+zbf_hc?9>zZk&nRC;YT<& zQU|8pNt5q&hF2%T5fmFK{n`GCFw^zwgtL5@vGR%fp-`{p5vjVH#7jZ326~}UWi(Tt5j{li{9{vxVb^q#lSH^7>T_}1k)jN8nFKoI$-)KY^=T26MFX_3cOujrG^);e# z;s4O%uH=@=+_^kckHHdW=FC3_E12;tYR?L%)cB2Lx#Y2TU#5?}`!jLu4Ne|=Lz2f{ z`HaWjq0q=D^=y}DRU9hKqZbTH#m;!qeN^-UPMSN*e|p+WeUm&~fyb}o(_K`hsT_~5 zuyjXI{P!}xyp??EA>09^eRnb(mLWxZ1+QSne=qd|ap8lH?LxoXu~T8+*hk{p4Ja4( z-V9$GiC!H|{AhHF3|sEHj5iS(UpPyB-7_{M+^D3!_8Q(SzAO~?;dNL2N5=Lix1))* z-&{wg-l=UC^Yr|V(#>?Vc#7xb+^62HWbXCNbn{p=`7TS8aj}%kqE}JXzogxf zle=N8ld}JKPGiShsD@4K-hVoxl7;xR%`az({&oK<>Ivju_Ycx2ZD}5=HWOt-vc}S; zi0+3HZ64=K&9ka;PFhK1?FbrEHE~C78sX23!InnEHEJLSs?9m@^t0DcbvoV zkiLh7c(MFHQ2;hg)BYi`#jfnup?{wNeL4>;UuS5z`RvwVK>6BDuPmQ&)DlN*aI55u zcD!_@=U=z6$xK1|f9GlF$i;iQEt$Mm{BM`=|Lob&Ul+q^=RQ_2gp@zuUq$6tx^dII zoFCGaxF$S8aT}6bmx$f4d)Yiz+J?kfX&e7YjdN@IvNm3bh3i|xq8%*kQ;F7yM=oGP zuF<<8zK}Q|wN@!nQZ-F8_8NC{psf+Rd+s`5K zeJ-?9RSz0!9m8&x$$c+cA8Gf$|95AR=FTGRiisWZN4~)bKPBR}@2$Tvc?+bSEre5l+GUx`sa z933>tH_l=OdH7;<=)q)2Cyx|DokMuT*_g4`;fedf)PgS7soeknI^i#tQafE2vr%E8 z!@ECSGsRYL>UC1OilWq8(pcvvq3^Z~eYVE>Q>fRw*}y&>dg%5fZTBY8z>3?GSvf*=!{%#u1{GpLUSmw}TrD*sDVLaPdbHcyLIkq$YRpx2) zPUe}Yw2v+E5#}~_d}Msyk%=#(?fWq_g2~j^5?;&1rP+hY&h;a04+TZ{%Tq7S{_1N9 zD>IRG>emt;^&?T%HB+20Uwj$uolGob>emt$Bo}h6>HZsEOL#XE zGxT(FgFcg-p-VHY^*gUi7B=Cyh(8fc)F{{T9i^tROSZPLrby9eyyG3tg=gnA6Mfko zagu-V@$>Mlj4aU`yvdET?#~-1ehTXy8r``QP5oA9do!B$0ZC{Y94`@~K~bgoU!2C` zBb1LmRv+&*E3AsB`BSWbO=*|&h>MqYjl?>e|L>fVGQP7ETiRr`YV>FMFw5~f`q`-$Q3)7Up0e+wj7neG$4G?$l;b|DK5kAE8;i zCW7rkLc1FW_3soqt`L&R1Ijn=)1y=GaM;Di3k@@)9t+a61Np1>@?%*=W2e5YUpx9c zwHh_lwuTIS!s8$2*eE<1itdO*jwVlJ8Na%0k~}nbSaBJ;5ZDi_XRC{*QQ7ZaYv}39y_T0UliouXJUr-7$@5Ken`&HkN;$b#{GjC`Y97L zv^P0JKPP8s-#?$B?)yz+Ep`8e{(emzFv`F99?#gpVVr199W-`yEd5HE-E^@v)%}oZ zq8a;*{(i=OH_E@5u|JFx&DddzW-QCzpno`HjhvIB8Ov(wzn`(AjPfsLY?Z63XvVS` zJDRbh|H+K~lbt<#CT3_wa)ypc&QOm3)(jn+i5Xg*oT1~AGnDhcHAA^FF+;Bz89E_3Lnr>XW+-naW@u(|hE7V(P`>|shK~Goiu{?Fp_7dh9c2ZQ zGt@N0Ilg7Ja2e@FG&C}ZU+1Z23ipGWvL*U3k6qD))X;(~G=C4Z57Aqp;W3eyKtoSd zJv-^3Ye0DM>PGF-LL(48U`40VL`$gfGC{n0CDy%lEYGqzL)*em{o`#ELjL_j^K`ER z3<_5dJ)>ucoxAl9H`g^A*9+}{diGTF5qm@ztMdrI*sbT#a>?I}-k4APQhdHcr{Pg0 zY&f%rwsXBwE^pqvak$KjDq+)f0R5Nk8GU`Ie49S8|Dw6C9esE%^F5g+F6^5puO3dd zNitkL9C=e0&cvoZJ-MmRNN(yD88-Eu^;&i7DZK0( zKFkq&G!VKD4OLn`*3mBN1NPszp}9J_+(k3J+-GHCx!Wh-crKn??v@#ryXCoI7Y~mu z;_qk1uL|S61@#a6Q@98XjW)y5r6g`}lKV!pSa1Dpqh{gJZWAW&9jfo_qJ6`c)k6JA z`J6RFRb84&KV1jLpMSS)>yOYTr+w%Gtw-4BV*S^LRT;jr7JUaG?2tJ_UzFx9zI!^t zQc5`;cJzN~IpqkG)2uXpk$dbs)PGpK*~%2)aD&E5{3qVL|t z8h(YAMmYzBk357i{@MDuvrwlSIz(TgE*si$b!aoVUC$T>wvXRaT$fyP=VyA&Rn5ej zdnkEfS}nQeuFtUMTCNF4M{VmNw)UceEnefCag~I%XT(?+CDGB4*o#Ys=HuaUT=<5e zHQisk&iDzWLwNAjNtJeZq8C1+*CRG@e5VWhTzq#+Y1XcDn1fJVt|@)=b;WQ}NwK?z zO%c5+7Cvav$D14S3a^_bZquT7xKg4$JbC=4-O+vX!bT6L03Rz4U(t?#_Q7|c<6p;! z_M7;UD+e;{Lm`I@O%LMpUp@6>BC%twWa2(Jtcc{TZLC^hVE>`1%?e`Ehi)dK*XRDW zdZKo?4GpbY615ZIxocRMKW7!K$58k2cZcoRMAgL2;oGN)!?ta5=c}ISov%hFcD_rD z6J34OOzwQ`GVFYJUf5L0bmZ-p_ykaVUH|6=REOk}UYhA8t(%D@y*#<3^^!~4F~gF! zJT)|2zpUq=0bRo$9*&4mZy(#=l1**G1(^2vF0SIwe0zj93@=EjZS)j&cyyLU`IOqp zFSIL?`t*;!9uhTDy&zVJUoIuI5)B_X>EV$>XxbF=i5-}UwAefHv6;~XJNCL|^y0k7 zw~pWN?NUcV^mVR2y<_=aHYl_$jm=K{8!vuHRnNeo;mbndm!iG~;%|C}o+(46V>s1D zFPens5#eW?5c>=o7*(~k4`<%6WV$|fO6=Apygi|^DBid#wgM6-<#277_I><@gL<_O zGZ>yYM=La3yM&gmp=^phn@H0wcw+jnNMZUTzh7?MMCD&{^+cH_Ue<``nfg2@+6--? zaZ0AH=GcLre6>oZVS&T5QXM;WyeYBelfMz4<$v+eG~H3#J)3%>sFJvIa(8a@=iND8 zNqlwgELkq^)}?Fw$~(T#d4VL^ST3~)OD$8wMRDv3KR!Wd9Uc2xc<2W`y4nv-852*g zLOI{LUASVAX;kXFNDP}a>OeVpDU!A&;DDS1yY}xhs7tJ0?f5Hv;ajK!L(eR|JNk@D zD5yi>te}oAw0sw*rdq_5dQoj*cwNaOQBRDzL|1OnxhQ!?2>D0d#w5={X>T~fl3fwr zo$1i7Z@cL0oMD%V70cyPVx!e=+M-UHHE}`m0dCk=JC+TvDTLdA(2yqfxGL;zQ3)RH z&E-;>HE9sFm62&U*LUhLD7*}0x^htyR)Z}|;;qHDZEFnZ*E3ulM)l_5>2R#Ad%2Y6 z(RX4JMMLT=q%C)QW z#k+X*bK+=^N{e_wntCb@&uY@|N2!CN*-XFVCCZ`HkwSOpnXArDl;NTL44plvRzWLJ zQ+t1oelzh>acC+Zu69Dv7`s{u`%P4!#lH4bFa4{__P==dIX-=2BPzjlART{r^OrZX ze^Yy2UIw8@;=#$jX%e&l@8p*8y@!xj`s(;_5QfXL{^jCt7g{w&h`*qy^;CEZKP*<* zFGFzKcx*a5Au`_3b&ROSj$?28Nrn(wiyhq;R1DrU$h`U*mt^`m%+{yE;__YCK=fkQfpcA3;Fqxh9YsE_K^ zsiV#lQ+jtAIHXVi9?_2GoR)sRYP!>is<3S6(29U!&#?OY>%+{Q%_-uV%z0R zw3?!&%iQh|s<`^}A5cEo-dR5q(KiYQ42qf+bsp3+$}L_s75g?^;tK1$b1IfiIq#fG z!g(Q_TdizL<#W#Q{qWFDML07L3N>}HC5bZW*r!8SD`8!Qw~+gE7!>uLADw_YhTETR zy~`yVj)vRO_$_W&Imw+XcE1~n=61bx$ZqTOiS_T$H=O9h=`yFQ%h(o8+``0;nNUzh zC&%`=UB^+sqvt>%j*cQu#T}4|lX{U`&gRx1Y6Jh-9nC8MF&4P!^fOehxhiAyK z3HpYg3AeNMmdm6@z2b$#HP&=5cA1dk+9K@_Mo*Yy(??9b@lE~B*y?xTrPG3Rb(b#E zl809O{5&9~@z8LA-MfS@M#jq1*usWY-M>$0;}nWT#bokcIBb+yl}EV0jw(r_b^I4~ zU18fF@dCPZ>89aAC>(cDfgMWE48L8vbod}-@90x2u_Xw@GgPCc|CB@QEF0@*#@p$k zN0HDAO}(%{{bQAowZbPe!x^A<@-92t^P@B1Up_aEl|k{mlE-!JC^>d}lX|8P*ZP0= zYl&7|>92BQS(W_z%bRpn1Dg27j-=Gef!NLx-YHJK--uskW~QPbrNO|CiOK?{Z~Ei3 zeC*InZ3n97dY7=_=QN5K7_O=NhTW%AMNK0rSFT*{oQmh1lgO`FO1wfem>Iobrd;smM$F{X)Bz1cI(`!!_Y8R>C%aNs@QymMa-}v{>j>lwt2dWmE^+w z$LsDtt7ysRk?96cT{E->`8+(BFEwiFO;byDUlrJ}`=C%4)4fS1su^C8WmKUOX}iZw zO-=U?pXN3*O}bVBv75zEA@tYpwT>RtSIeroP~f8w@(C|*4dF#0`)45>B^+22!akwow;>c4hJ6{r4MOkL zA@mUPt_UHIkRpTAg=|Yh$RZT9sQE(}bZ0(CS-Za7LRS$z+w^Jgz2yN@yDtxGv{ciu zTfJu0t`t(&;8|AVnL_H?PhB6WOOm>#kF*9;*TUg}A*1X)UBDi_EYzS)NPZx4O91y)O|5^`=@TXBkc*PTRnA8NZn@-?g|UIUr0R$ z*4d2Pgfp9m(8>WTZ4ZV{txGIFnR7%=ZT4B8V=uMhMB z5}iRSsTi%HQna2b(8pAnHq(XlIn|^usV?oHD`_`1qkYth4pJLBOr7Ys79sSYJk*c! z(GWU~ZlW{kRw_l~r~*x*^JyAYrbB7oEp&rx|$YK2U<#9XgLk0l{Avp&{$eW z6X;`_Ok3z4+Dux{-3yaLP?L(}^^a z^3jtmLMT8_Q(=0RiqZ>Il3u3r^ctN zqOSA|b*E>kCp}NS=|$>GFHoqdLl@C|beAbUq)ViIOm%1rHKFZv744ul zw1=kCe(FjGX{-GHpwB7m{t$W^dK?{+b^@K?{egni-_Rm-BNeAHREF-RinNNV(!F#c z-65AtXqL3vG@I(tqtuAzQ44ySTGJA0M@y*-y-K}kBMqdtX$WocYr|=+v=LOp^MEn* ziL`Na*mx6ZtF+1VHBG0TG>g8cITU?cdM-s@xm`%nCpMQ*^x>yv6n)y`HHto&u!^F- z=GRix3F&%@`b^tIQD07=z~&?;uU0Kj=xy);)yDbPSC&AQw%OmWO80Ni>rR&;xWT&89QxQ7T4HP)S-U zzw&ggFj{CUj1@WxKBofolMD*c*U}2pf~!L)LR$?zi+(h;1ns5Lw2#WsFLVwaq)K#% z&ZmV}hft07(wN~PJVWECtpSs$J>5+mX&QB=8Pt_#Qg?cQdeUs_O^?uPa(0|ncKBbRo8*QSmX*2DjE%XC@M*CLWuB2b(+?al+7SzPhYv>xfp0b#i_H-0= zp`)oM9Yg)-SQ<<@X&4<(BPkD!rW5H7I*BGxews=J=w3R79-vcc4i%=y=?t1rDfA2# zr6qJ0y+~)%a$0MNR?r8uib@)~nm(2G9+j5%0d=B{RGv1|FUH$OC*Bysmvj<+L-}bZ z6`B$g57PPc2bDB5+sz?Vq#RVu&|GvOok-Q`WU4`@QcWsCwdpLn zluA)uDo^#O5?w)6=}M|jji?qip}N$J8c+*rOjlD2YDL#jYr3AUqYl)Dy3!+-s29zp zzSPdp0kl}!4b(x}FnWMSQ5U+6-ZkDG^bJj*9dsA%qA9eS?x8(&Fa1dO(_VUzddm4> z>O+syVMFKBvBN@mn))01EDfZkG?-qY8|V$Xk=~-=w3cqB4`?KPOt;XdbQ^7>G4wT! zrCoFf{XlopKAK1e=`Q+%?xt*Nm!?tb8`SdO}S>b4*f^dv*u5k1q5{?%t3wedA!q0x~ zBKm=9P%)v7@RPK<)Kb_fTr2Dr+6q4i9fiF@S7E=mnKnunnDF>I-N>0>2!LKQfLkpqsQrNdXh@fLMls(sRBJu=h8B& zOv|Y%y+Ie!D!Q1~P)&M|>d<My?Vypgi*BXeG=?sh-yL)+ zO`yYu-c8wrX~HqW3?Zj5Q#ivE4^VM>geuTHx_}l@b$X62p=DHuUZsVuq&qrXo}(`G z5_PB7sTaLPeQ7NXp!GDE>dS8^ZId>FcF--HoI2w@r(q8U_}W>N}0 zKxffxDnXA>8G4*5&^&4^=LOW17Ew(@pO4aL88tU_IbB67s3pBk*U-CkEv={PX(P3z zE!3XAppNtnb*AsAEB#2_X+QO(L)4qH^besg9Zmh|I2uTKXfT~bH&8*kkqXl>JwrH? za?@Ef+|UwqnzXWXv$P8IELEmks4BHI-oPid-E<}0LyhTPYDTxo z`6?Pit!bX2ZD}cWq_Ku}r8}q>-AVmvA`PLtXgJ+Xqi8CPp?hdN&7iyJKAJ}N(|z;+ zJxCAHBlIxMrAKH1Jw}V@33{I9(K4D(uhLVrl3Ml(VKrS(Yw2l2-=`kZKB7g^Hqy7W zm7b$*beHkIp_gbUEvMb|2K_**XfLg%{q!y!q;>Qgy&&hq^dcQSAcQXrJ&t~)-1L&6 zdFd4@K(Eqi^g5-`3Mx)-QfYdND$r^=kKUnb^e$aY?^A91fG(#G=}Ou_O=%-tMW0Y> z`jpzzR_aKfQ#bm8dedFKLl{8!&|vz~(4jO(+RgN}v{6*}nhlHzx`6JZi|9eRm>!`^=n1Mt3#bk)qRZ$xx}26#eR`F$IK`}_W9ThvVCWjk zC+&S|C~ZBZNZUkBrF}}}rF~B4(RR9kzNSlP2VF|v(G|3Zn$S;l744@na{gj;2+ip$ z>gd9H7u`hP(+K*JM$yl7EB!*F=~ud)ey4GiWlRWH$vGRfq+@BCp}FY+%1hT6dNN&0 zr_%LQgxbu4= zr>=A}^`eo~pYo0hVF(qV8|fB9Z=w`wx6p0UM$?Nlp2pHdy3lx2sX0xjtLQ#zNwer0 zdWf#2IdnZeMr~;>-67}sbSFJSJq>-12GNT&(a=}uE_#FRrnhJ+t)+YD1DZh}(|z&tvqGDC4}=R8&#oW=mN?^)#)WuTtdsK4!us7(@JVU=b54jT}aKT z23<|H=^DD6uA?idEgjQ3gpTx<{JKzkLwnL%X?^Ge8b}*xD1AaV(^k5bzM!%6HBF$M zbT@rZ(`g^wPlxDX%4%L7r;X-d0d1wH=?hv+U(<8+9WAAw=q1`u%js8oo&KPebo8wu zyhS-_4dtPC=_Fc51?WR6OdnHG`h?D=EmWF5rwX*4&ZDpC0@^{B(05da_E0_gi5k*A zYDNdBB^{#c=nra7S;vOZnU1EOl!N+HP8v+PX&9YIqbMJZrUEpM3eiL=OjD=`-AhI3 zK`KsNe$>*YDC{r zQ~HTo&;e>mzfo&C>b4NBr(>xd<))65kGjw))Sb?tUR0F&(%CeCO4DGfKtt&~8ctPd z1XZV7s1}W;%V{h%r18{@Cek%DncC7+I@VD!opRGG%13kPPUFp^skDIZrDx~?dX`Qx z#q(5{UZNCwor=>cDoyVM4Jz6FJyWlf`l!?osRDgWm1zrAqc7-U`j%?Z_jDQUrTTP$ z8q)97l#Ut~!c~-muA$@UdOC^PQ$gxXg{eE8NxkW88tjsw4E2{*K1!oXbR$)zo2fe8 zLbYfN)ulVA0ZpVPbT?f^)2TJxPwi+nb*4@9_ymFG(R2fzrl)B!Eu!b>Sz1cZ(M$9K zEvFahb$W?b(p)*eMNiUNddtucXdP{!1%`e?3+Xd@hQ6f5w1bw=_w+pNrKNO$meKF@ zG95KBgyocjUZdmb4a!R^sQ|5_)97s~LTl(OT1%zqJt{}*=v-P)RjAG#AzVcDs0Mvx zXf0|ctuB2mtv={O zEvG}Yf_|sB=rFCNtk%^Bl#Mn}cG^ri=yN)4d&&_TM$ z(8E-lj+zugUCK^Z(y`Q(a#2glL)TMY$}7M8)I(Y!>Pv+wzoA8_02QNCs05u#W$1QO zoI?|-65UPb(>+v;?x&0BVX8reKuYZrM*q{=^bi7?@>egfU20{BWf;fBV9$CsU>ZtYiJu?OJ7nq z(|$wkrR}7Sw3|B957d?RQg_-B%nm!*}Z`=nK%`{`VI$I#03u(Yc5 z2wg~<4ZWD=NvlcosSfQiv@R`@R-c}wE9p6EOoxrvj9!v<6}>{OD5w4ET3R8k4V^5l zJ*}43i7u7amEM=ugFc|%^da@54K$E8(h&NDZlq7?CfZ6P>2tc38vBhg^tH5cG)~%` z^qsUx^gZ28#T}v3s07WVQZ$?HG3^tS*9rGYy428xRE8GO8HPSj<)pnx6=*qCq!m<& z-lod*E>)rRRE;*$g|vmL(-%~OzM-15n`+ZfRF@9WE#~D9swXY$TWsq%&wYrO*#FP0o91Ivt?0hW<{KDeD6v+-qodnn^in7M(y3Qhu6Eg=h|)L61@~ zdYnqqTq;LT(z&#Ns?b8Zh@PREw3sfVB~+iDr$)4tn$t3BNiWlNw4B=06PBn8Euil7 znxVbud1?LW4QYeuAPu8cbTe%?-fi?7-A;ed9h7BC2ovZix{HpcDRd0oL&ws+^tPPu zr!_R2@*DaXolf&;t)WlRd$gF=(F?SmUZ#)eb^4fA(I$F_Hq$!VLL2Ba+DzN%bJ|W{ z(O0ySzM(y|gZ9!cIzYSWciKZ)9}M9~I)?UAF1pJSok-ItAMG==06i$}H2Ou_8C34N z5YD1Q(n`=Wxv=Qy2W|Y;u zTus^NTFOrCCz5UNmiszy2KLOOw}Q+}#J zg{UT-LA9wET}maXE|sHtbS_;%Rp?5(h#FB%YC@M%GpbK5s1aRF&8QW%q}FsDT{10% z_H-$AqU#LpN{yuTqBhd{(li=O9cU=^Fy08djc%baG@8cJSh|D8)15StCemcOi>6X1 zIZvl9bU)p1=xlnF9;I%E&ZQpo6!oIT)Q4W6e)KX8px0>-t)e0H4h^MsG>kUTO|+Rt z(C0LYzM@-cCyl1>>2}&nA(J{6^^RGcoLl5`Q3p^K?JO_$5LbT3te5wIpIXwDbPYA8 zYpEGsPghY}YDK4CAHuaX)9<#SL56msq12UzQx6(Ry=gS{qdRCIO`;(*m2RYa=_Y#6 zZ;YflG=?6hJLyTfn-#QZ!BmNcQDquQRcJI-qdVwA zx{IpQJye74r<(LI)ut!tQhJK&(z8^LUZgANRl1Viq(<}(HK7lv8EvE%w3V)=FR2yn zq}H^DuA`r+4gE^(D9fA>I?yrHiH@f(bP{!=Q>X`>LA~fK>O-ZeADu%3=zJPP7ts)^ zMMLRw8b%H2CTdP2=o%VDZRu9(OrxnM-A?^!9Nj?U>1LWhx6vfJgC^5mG==V=X>>nL zr-$iYdV*%sQ#6a7r3dLnnoY0L9D0+!oEgG9^gF#rS?&+v1Ik7pQ4ZQjxo9(;KwBvv zZKHzpB|YjlzM;oyH_fHJ^dudm1$3AeQuaqec!qM)Vmg7A(8=^Xok~lo2rZ-H^fHy9 zpU`9UDb1y=G@m}Fg|tP^i|8|Yp1w2mCE7=?(l$d^&~|#8zM^;O8(L30Xd~^S zEwr1ypgr^r{Yc-@Uiy*t(SG`c4$(o%@`OJrJN-__(P7F%S*@#ll#L2fb}CFc=uGPR zXb5LhFDgaH8CsTxNUKP>q@72jq+LL_Qgs?lm(cB0o5s;)G?D7jUDSZ?rbaZCn$kVg zf==*jEoqjt>*--?M~_fPdW^cz6V#pNQ7@WLed#G0KzZdnn4Xh1oL-?3w1RG-)ij#k zr?Iqw#?z-Xk+#uf$|vWk^u4tEXg|%OL-Y`3c`SrERKOIEQEqATDL*ZwQ)m$trX^I! z6faOoX|GUedYvlJN~%b2Q6*YKmFZooLhGm+eMBYH6K(q3v#`{^n=NN339H)=^o%?%-iveRJW<)RxX58X(4X*lJlo2ej; zq*Lh@I-PE#6dFUtsHopKo6e%LbT*w!C8;Wvri-a8)uHlKkItcnbS^cg^QaY_PuEjb z>PQz*ce;rB(#15GE}`L6i*BJhG?p%-iF7$lrTTOqHK2#6Aw5QoX+CvSWGtf2w1m3S z3)G#KQBQh>dedvvmsU`JT15kCH4Ub<)YPxNPt9oqT}7K|mvC5Ud`k#N%?sfte`cqj z=~&uNx#$4ppTnR?t1PiteM;^Z>1; zhv|KKjMmc(xon_Ww3!~E&uJcgO;6Ex^ep{IFVZjc8vRahQnm#lF6|LI zWoZcWs3I+(O7t{Uro~i+o}+5ClrE%~s5&jD!*YI|vih}ERL{_Ns42Zq*$n-VvePEY zL0joK+D^IXTgpwl=>+>NMp13k-bQt495tYc)R?AFbDBX{)BSV} zJw$EjF=|gwQYU(vy3!KrL(3wWgV$&Pt)L;aiiXi@8exjH)Sup`35I?|lV~GNrp+|e zcw1>WZKu7$q*p^2CGBpS^;8HmXtcDMG?pHs3G^sUrg=1#o}%frh?davRPLn^UZne^ zy-d}my-t_VN~%q7(d@P%tf5EfeR_gEqWSa*Eu^iqh`yk8^bL)c-%fg7+HRUA?Wds7 zB5nrnlRA?Q(93N@I7F{fme)d9LD}go%1LV|54}q#(f3r4w#e^PS}*N%+9j=MG#(YF zPYf+dTc{juqjTviszTq=g|v%m&~fcTs6*$x970|C!O;42v9v~1lbX^#LtD@Rx`uwE zHk74Z2puUKb){pdC!Iz8=vMg+q+HU5&?ITY=|pKGC?DNM1!)|eMic1_x|`0Vd*~9H zN$ceI0G%yuHhm`TG5Uh$QdvXiQw4g4D$#RPg_h9;w4AEb8`PTKqO)EJVGY%i_AZ?( zZ9P?{4OG|AO;n$@QbXEKP3c=|LElkJ`hf<~J{lsw19Y9VLv*XOEU$;qPFgnVNXJrF z%1u4#Be3_BfaX#YD!V*{ ztLQ0dt?20XA+({34Q)@)8rq5KO6yK9Nb5x}QGa@shR_NcPH)jDdWXi)`!t@`(_OTI zrqL%fleW@q`idT>owR_yr^WOWEv5amoPMR1^arh>tnEX1kB*@a=~()NHd(gMDW|mU zbOP<5{IrJ((LOqZ4pA}6`bG#PDF>CK+;lGGqbgL0YETiXO~t7$m7xYyk(yAIXsFOn zs3!~*&VDR}A=F5?QD`FEBs3F73RlbJR?2CLadf?Kr_ffIB(xXq7FwBN8g&z93Qq{z zgx=jk_>vY1-w4kLJB8M!-A&igPqbV(AgmM)39E!Zgf_;@x*~*jbPN>`HoY0b0BN66 z2l|YP8Tuu4lJ*UCpO;R#Kgzl)g!=N!L5(OUHKE+pj83E$l#d3P zwg3&G(`YEA&@d`aH&JOCLFH)_olCb;RT@pz>2|6`f$IcP@n=_=Y{Xe(MI z?OJ-4+R$^PN5BK$_q;hR`G$PLpXAO`$O~jmFb-x{L0m zX*84WqgnJIJxGtxY?@1R=qY-X7SrSO0?nnD=}CH>7SJkMNbk@yw2l_j23kU!>3RB` zmeNU(`Uz+x6`j)Pv-PD2hQaAdA z`cUZ(Aq=A5q}@OpOLU+?4nnqb1rZXb>-I;VW z&8Fk%amqsr=pZ|NrUx0^=L z4>XGQ(yg?gM$k**Pb7OoY35v~`071|2F3+?2WWpxN0C_8nc zoYaL*pl+0(deAA9tF5UN7ss5%X$OK7iP#qx-2NJwRRPA?i*OtLY({Eay3N)SV&B zrH>4qPao4l+C+=>y758z>)rLWSsP z%UgtQkwH*-}_9cZ7l&h!iQpo7$x4$&a`oo=MVG=j4F zjoT<2jic-|k#f*fI*#t6T=Wp-rpM?6nooIY5#^&7=wy0@3epNHL~l`d^y(>T z(24XW<)aFwC_rna6{ZiU2z^LJsiN_UQza@*Ul@80eM^;SC!J4~jaQAT(8Y8q)uw86 z8J(~&gexc?HKan+go;peDo$6^g{HlRs#9C4L7k~4^`zR=pE}SDRF{TPJsL$<&=|Us z#?u{s?Jk-?Q)mj^Lo?`JdVubyIrJdSrH5%DJxb5g6Z9fINsav4Q`Cf>rDpUZwV+q& zYI>7e(L2xj?nGimvrP4m3 zWwe!ErY~qYeNC^?PI`m(&`SE59+Tg%w19r6MU>_F5T2)O^b#FIZ=3cwT0?ngE#;&4 z=oDH_=zrpmO1E}+lo5-RD6>@q4x^{4_hpo-LpDp6CaOf9Gi zwWMm)nzqULdb&hfN4k`{P+jUy^{5wJL4D~;8bI6SGMJi68&0ig1huAH=sFrrZD=gD zqw(~WTqaUiX;Y~eO{YF|AN8YIG=LtWK{SVk&|@@|=F&HEo=>BsEuyisgeK4nG=-MY z40?rT(QEWDt)Rzg70svBw8L+#rCqe1cGD)>X1veoTiPBCrLSo(?Vtnn9qlpg9?Ib= zZ9nCqgOs0sqe66;iqKJ>?4Cv0sT3VcCn`=`dYGSv!T$ zhO$vd%1+%V2lb)jXb^pA4u;VVx|w#-DB4Y8q&qom}*jPIoF{Rs2*KwXhZ5mO)0OTS5ZD{O(#=ZDoCBE5Ot^1s1Kb^ z1E~lNr8DVfDn_?baT-e{Xabd@yQvIKr*brtD$qkzkshT=G?yyVQ&fc((?(150&SsX zRL#&==o@Ko(1p_8q#vcdLqnv!M>XgJs!1EEHf^CxX*<=W9aN9Lrz>bLT}cP15&ce0 z=%~#hG@~5Uf^yTp~W4v4GXBtEMX&fD( zJLy-NM2G2a%KC8#(vib(3Xy-PLzYXP)_Pbxv4juNPQ_E z^``LQgxa|m(ash zn;xOd=rO8CPf!DzM~!GcHKnJh1wBnIX%V%iXX$!cO6_PFb);9R3yp9rbf;0&n{K84 zG@1s}22%{9&2%$up;7c1jixVYEPYMm>06peJ83d~M^ou`zcHQmN}ELo=pp)*=Fo5S z82v$WDa*zX=F?HMkdCHBG)~S-D3`QlbRxY%C(&z^pH@%-T1BVOY8o$>wNymfdODLn zrsDJom7^_Gkv^x&w4G|w*Hnjg&;&VuN0aDBdW`ndWI9OQOz{WZBQ5Ke5N1*inoYTB zDdndps31)-ZDE>5MQDMcMd@ieoAyvyT1FLUktxoj<PW{^UMKpz)LB}7>Q1*&1>p|i zCTSC>AKgXOg=xY7X)|aLJwQX~5gJPKXc#?BuL(PaTcz!$5%dGSC+rhONjpH#({W#f zFq)31Q$GvgB&tLwQ)N1Zs?cdvjn1G8=}f9lXHg9*LAT4fG>xMQbcLbkQFE$F;|;xt zCQwb9M3>QIx`L)qW12=SXgXa(_fi|0NgZhxb)yHVH_fI2G>2}WN9iVdoJP@H8beRg zcv?V{Xdz9dXXsv9Wr-f3cW8D{Xc4#A@JFO>kUE!^NSjZ)Xb~-?CG?~5mQl{nLwJRb zr`ISCt)LTW6`e$@DL<{H0`xvDlk<9dnKn{UL$^>F+D6L_{fb_ro%9Cnp_TMAt)heU zHvK_sDBJcB*3z-`9_6NWbP}zng7gs;rjO}N+C*p5W-3ivr~-XP=g~H*O53SAeMPnC z0ZUYu=1_h5#?UKifwZQyLs|>kORZ@)T~BWsuLHeBooO|7qdms!Nk39Q+FeNKAv{Xi=t;^>Pg4$BLdVgI zl#5=aQuHR}rnl(?dXMtbhm?;tQLAksY^Bz;jjp3FsSSNY?Pw=;pxxAoexNS2m%7n@ z>OlwTWWV+s6{MrS451JmOQ%sDI-T-U5jvI5q#{&|&Z6Q}no7_)w9v1ePt~PWqcU_6 zy=-VrDkrTDRiOG*ks4Dax{4~(wN!=LQ#I;J7gBHPM~lA>p$0ui4;k1jQU8sif*NAX#lmMe#YxagQRt#A=HD0QXd*d1L!6iLL+E6jiOOB zLw>i@EV_dpqzN>e?xHy~g+_M?;U2o3X3{u%h{n^SG=b*PBzlS_(;}KeFVHl4nWoe0 zbT6%+=zV&d*3%l=Kub-#i9VF}Ic=itw3)u9EwqD{ znc_S8Qrb_ngZ9xbIzYSW5WQ@QKj>#^NAC*ZAmyM#l#_m^+;o^uq^vta$Vb_z0A;5_ zl!FS>aa4qIQBlfG#c8=YC`qqTd3u8?(MqaHtLP$nn=YX>bQ!IsE9gCHOzWrxt*6%X z5w)XM=Aa9;rXF<2(BAYh^`mZv4yH}ghSFyG|0=T6&=BJ|4&at6iM7^98kW{bn`Dx! zIdbJ{gci%WOqd)ClQ!0d=4g(PHffG#Ga=VRMl`wMG)8MpYA4wn5(3wR86hvF}0*;RA%=DwbH$&Fsi3E)Ic}PeWOs_ z59&&d6iy*kLDZUSK@qxE)Sg1A14U3r>PV3kMV%>zq9~SnP&`FbUrMVCVjwN01X@W$ zX$=jhEE+}GG=_3%JjHlkB6X)JG@OztmS$41jTGvsOQl|vM!jh{^`Q*vOIg&9a%li< zqCvEc5@%A*~0lJ?S8Dx}hTK@?HG?htLKQrbx;XcwKOJ#>-w(p4&;TeOev z(tdhCMf8LY(sMdYZ>g9*QVD&dqx74OQ4_z6j#CRd;$?3`r*vWUL6lQ_dglZ6DTws+ H*>V2?-MtVM diff --git a/docs_local/_build/.doctrees/index.doctree b/docs_local/_build/.doctrees/index.doctree deleted file mode 100644 index 89d0e4aeb46bf0b3939e11055d61dc89a4b85bbb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2971 zcma)8TWcIQ6n1Ry&FfuXl9ZCf#e_CZO4m+93nkDe=gqANN!q+P4B8p(N?0RJC5^p) z2s95VG3cQX_5b#hW_G;mry3TIj*iatJD>Dd>)(I+EAh__GnJN>3v(bqe`7p?^d9ZGA=SrrGMwyit8AfTCQZH&swa_I`8~rY_=@7f-Y>i zhcDFy)h`9H6Q%S07sQftstw&g5*%LlU%xo^;WQ4fzoD}b#{W9=(O1E?3xd$BXWOoq zG`X-f*RE4IaqEIh>QBlsaT~;1&Bvuh>LJxNo*AU&I;kMWK#1>_Bzg7K&B|iFC|$>r zX;^tSWcOH~-Dhj;fqQ-Q{PcH&j84gzik!m^JAFk9WloneA2pd*iH}}hTBhVFLNVYT_UCvKC=RFoN=a1oW*FT{0oGRtUT znLl1d`A88l;d)0(T9`A>y<49OHTGAo&3KjpK38r~8)^hyS>|J^Y37et+(rbCQBqt& zcLONAjw>$bUf5B|9SK9lPHVPj6_TYD|LQaewU0#lR9%KVw z_Qc(>nn;6iFm*|x)l^ec!MA0VC8DZbtrJUG24A<_FfDZ$j-(ld&AFr+3D-5$S}vy` zf}MsLCOY;o^RE2|5hYsQP(xppwkT~<5H$XT4NqKeN+s3AD($gA_S9IC7g+SQQqa_Z z&CIj5>sL`EaD9gDje1#Xfz<%*O2#BT3%iJ>6XE{ z#!*O1Y>9cTlv=Kp(Q!G2yWs`V5`~0@rX+`?hpweGq!i-gGL((VRAtceu2$aL|#K zL%6h@K4fB)wriNUqI4nwt-_ z83yKz>H|*gB>F%-4w=mbob@ajV}`SE-*nv`CL&K7YG@KjOc4-z<+jbGv}BerK4k(w zOwYPdVemFhPB7+wmf#eFn%$1;YFXXG5aSiFmokEQ0G?{vQSew!oIWdyc$ej21cV~h zM2A<#nFo4sp=F?fOWc)=QVZ3)DYVWKniDP(+}DA>qw}5mEd|($NkxoTZjH+G@LF-* ziju$q03~i?juEqoC3;FhafopT*Z{~ZW(zk1?&okTxZNdb^?rdE)+WuO9atGpAILjF zSbej%?2U1~Ah4GOB7E=J+bhHN@&66$8#h=ic|1KxIT{z-!(t@~(>d3x!d#S8dI%mZ z%EwAHU5u)CZU`Ig-jc=M+-p3vyJQ~}t*t-4!(Tx6c#yv zusTPz{4&FftG7a6@$O1#wgb>~U<@n$~ DlcAyz diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/claude_skill/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/claude_skill/index.rst.txt deleted file mode 100644 index 88631626..00000000 --- a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/claude_skill/index.rst.txt +++ /dev/null @@ -1,15 +0,0 @@ -PowerPlatform.Dataverse.claude_skill -==================================== - -.. py:module:: PowerPlatform.Dataverse.claude_skill - -.. autoapi-nested-parse:: - - Claude Code skill package for the PowerPlatform Dataverse Client SDK. - - This package contains two skills: - - dataverse-sdk-use: Guidance for using the SDK in your applications - - dataverse-sdk-dev: Guidance for developing/contributing to the SDK itself - - - diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/client/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/client/index.rst.txt deleted file mode 100644 index 3e3c485f..00000000 --- a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/client/index.rst.txt +++ /dev/null @@ -1,157 +0,0 @@ -PowerPlatform.Dataverse.client -============================== - -.. py:module:: PowerPlatform.Dataverse.client - - -Classes -------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.client.DataverseClient - - -Module Contents ---------------- - -.. py:class:: DataverseClient(base_url: str, credential: azure.core.credentials.TokenCredential, config: Optional[PowerPlatform.Dataverse.core.config.DataverseConfig] = None, *, context: Optional[PowerPlatform.Dataverse.core.config.OperationContext] = None) - - High-level client for Microsoft Dataverse operations. - - This client provides a simple, stable interface for interacting with Dataverse environments - through the Web API. It handles authentication via Azure Identity and delegates HTTP operations - to an internal OData client. - - Key capabilities: - - OData CRUD operations: create, read, update, delete records - - SQL queries: execute read-only SQL via Web API ``?sql`` parameter - - Table metadata: create, inspect, and delete custom tables; create and delete columns - - File uploads: upload files to file columns with chunking support - - :param base_url: Your Dataverse environment URL, for example - ``"https://org.crm.dynamics.com"``. Trailing slash is automatically removed. - :type base_url: :class:`str` - :param credential: Azure Identity credential for authentication. - :type credential: ~azure.core.credentials.TokenCredential - :param config: Optional configuration for language, timeouts, and retries. - If not provided, defaults are loaded from :meth:`~PowerPlatform.Dataverse.core.config.DataverseConfig.from_env`. - :type config: ~PowerPlatform.Dataverse.core.config.DataverseConfig or None - :param context: Optional caller-defined context object appended to the - outbound ``User-Agent`` header for plugin/tool attribution. Cannot be used - together with ``config`` -- pass the context via - :class:`~PowerPlatform.Dataverse.core.config.DataverseConfig` instead. - :type context: ~PowerPlatform.Dataverse.core.config.OperationContext or None - - :raises ValueError: If ``base_url`` is missing or empty after trimming. - :raises ValueError: If both ``config`` and ``context`` are provided. - - .. note:: - The client lazily initializes its internal OData client on first use, allowing lightweight construction without immediate network calls. - - .. note:: - All methods that communicate with the Dataverse Web API may raise - :class:`~PowerPlatform.Dataverse.core.errors.HttpError` on non-successful - HTTP responses (e.g. 401, 403, 404, 429, 500). Individual method - docstrings document only domain-specific exceptions. - - Operations are organized into namespaces: - - - ``client.records`` -- create, update, delete, and get records (single or paginated queries) - - ``client.query`` -- query and search operations - - ``client.tables`` -- table and column metadata management - - ``client.files`` -- file upload operations - - ``client.dataframe`` -- pandas DataFrame wrappers for record CRUD - - ``client.batch`` -- batch multiple operations into a single HTTP request - - The client supports Python's context manager protocol for automatic resource - cleanup and HTTP connection pooling: - - .. rubric:: Example - - **Recommended -- context manager** (enables HTTP connection pooling):: - - from azure.identity import InteractiveBrowserCredential - from PowerPlatform.Dataverse.client import DataverseClient - - credential = InteractiveBrowserCredential() - - with DataverseClient("https://org.crm.dynamics.com", credential) as client: - record_id = client.records.create("account", {"name": "Contoso Ltd"}) - client.records.update("account", record_id, {"telephone1": "555-0100"}) - # Session closed, caches cleared automatically - - **Manual lifecycle**:: - - client = DataverseClient("https://org.crm.dynamics.com", credential) - try: - record_id = client.records.create("account", {"name": "Contoso Ltd"}) - finally: - client.close() - - - .. py:attribute:: auth - - - .. py:attribute:: records - - - .. py:attribute:: query - - - .. py:attribute:: tables - - - .. py:attribute:: files - - - .. py:attribute:: dataframe - - - .. py:attribute:: batch - - - .. py:method:: close() -> None - - Close the client and release resources. - - Closes the HTTP session (if any), clears internal caches, and - marks the client as closed. Safe to call multiple times. After - closing, any operation will raise :class:`RuntimeError`. - - Called automatically when using the client as a context manager. - - Example:: - - client = DataverseClient(base_url, credential) - try: - client.records.create("account", {"name": "Contoso"}) - finally: - client.close() - - - - .. py:method:: flush_cache(kind) -> int - - Flush cached client metadata or state. - - :param kind: Cache kind to flush. Currently supported values: - - - ``"picklist"``: Clears picklist label cache used for label-to-integer conversion - - Future kinds (e.g. ``"entityset"``, ``"primaryid"``) may be added without - breaking this signature. - :type kind: :class:`str` - - :return: Number of cache entries removed. - :rtype: :class:`int` - - .. rubric:: Example - - Clear the picklist cache:: - - removed = client.flush_cache("picklist") - print(f"Cleared {removed} cached picklist entries") - - - diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/common/constants/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/common/constants/index.rst.txt deleted file mode 100644 index b843380a..00000000 --- a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/common/constants/index.rst.txt +++ /dev/null @@ -1,77 +0,0 @@ -PowerPlatform.Dataverse.common.constants -======================================== - -.. py:module:: PowerPlatform.Dataverse.common.constants - -.. autoapi-nested-parse:: - - Constants for Dataverse Web API metadata types. - - These constants define the OData type identifiers used in Web API payloads - for metadata operations. - - - -Attributes ----------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.common.constants.ODATA_TYPE_LOCALIZED_LABEL - PowerPlatform.Dataverse.common.constants.ODATA_TYPE_LABEL - PowerPlatform.Dataverse.common.constants.ODATA_TYPE_LOOKUP_ATTRIBUTE - PowerPlatform.Dataverse.common.constants.ODATA_TYPE_ONE_TO_MANY_RELATIONSHIP - PowerPlatform.Dataverse.common.constants.ODATA_TYPE_MANY_TO_MANY_RELATIONSHIP - PowerPlatform.Dataverse.common.constants.CASCADE_BEHAVIOR_CASCADE - PowerPlatform.Dataverse.common.constants.CASCADE_BEHAVIOR_NO_CASCADE - PowerPlatform.Dataverse.common.constants.CASCADE_BEHAVIOR_REMOVE_LINK - PowerPlatform.Dataverse.common.constants.CASCADE_BEHAVIOR_RESTRICT - - -Module Contents ---------------- - -.. py:data:: ODATA_TYPE_LOCALIZED_LABEL - :value: 'Microsoft.Dynamics.CRM.LocalizedLabel' - - -.. py:data:: ODATA_TYPE_LABEL - :value: 'Microsoft.Dynamics.CRM.Label' - - -.. py:data:: ODATA_TYPE_LOOKUP_ATTRIBUTE - :value: 'Microsoft.Dynamics.CRM.LookupAttributeMetadata' - - -.. py:data:: ODATA_TYPE_ONE_TO_MANY_RELATIONSHIP - :value: 'Microsoft.Dynamics.CRM.OneToManyRelationshipMetadata' - - -.. py:data:: ODATA_TYPE_MANY_TO_MANY_RELATIONSHIP - :value: 'Microsoft.Dynamics.CRM.ManyToManyRelationshipMetadata' - - -.. py:data:: CASCADE_BEHAVIOR_CASCADE - :value: 'Cascade' - - - Perform the action on all referencing table records associated with the referenced table record. - -.. py:data:: CASCADE_BEHAVIOR_NO_CASCADE - :value: 'NoCascade' - - - Do not apply the action to any referencing table records associated with the referenced table record. - -.. py:data:: CASCADE_BEHAVIOR_REMOVE_LINK - :value: 'RemoveLink' - - - Remove the value of the referencing column for all referencing table records when the referenced record is deleted. - -.. py:data:: CASCADE_BEHAVIOR_RESTRICT - :value: 'Restrict' - - - Prevent the referenced table record from being deleted when referencing table records exist. - diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/common/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/common/index.rst.txt deleted file mode 100644 index 55ae1b6f..00000000 --- a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/common/index.rst.txt +++ /dev/null @@ -1,22 +0,0 @@ -PowerPlatform.Dataverse.common -============================== - -.. py:module:: PowerPlatform.Dataverse.common - -.. autoapi-nested-parse:: - - Common utilities and constants for the Dataverse SDK. - - This module contains shared constants and utilities used across the SDK. - - - -Submodules ----------- - -.. toctree:: - :maxdepth: 1 - - /autoapi/PowerPlatform/Dataverse/common/constants/index - - diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/core/config/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/core/config/index.rst.txt deleted file mode 100644 index ef0f6d1d..00000000 --- a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/core/config/index.rst.txt +++ /dev/null @@ -1,117 +0,0 @@ -PowerPlatform.Dataverse.core.config -=================================== - -.. py:module:: PowerPlatform.Dataverse.core.config - -.. autoapi-nested-parse:: - - Dataverse client configuration. - - Provides :class:`~PowerPlatform.Dataverse.core.config.DataverseConfig`, a lightweight - immutable container for locale and (reserved) HTTP tuning options plus the - convenience constructor :meth:`~PowerPlatform.Dataverse.core.config.DataverseConfig.from_env`. - - - -Classes -------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.core.config.OperationContext - PowerPlatform.Dataverse.core.config.DataverseConfig - - -Module Contents ---------------- - -.. py:class:: OperationContext - - Caller-defined context appended to outbound ``User-Agent`` headers. - - The context string is validated to be semicolon-separated ``key=value`` pairs - using only allowed keys (``app``, ``skill``, ``agent``) with values from - closed allowlists. Free-form text, email addresses, PII, and unknown keys - are rejected. - - :param user_agent_context: Attribution string in ``key=value;key=value`` format. - :type user_agent_context: :class:`str` - - :raises ValueError: If the string is empty, contains control characters, - does not match the required ``key=value`` format, or uses unknown - keys/values. - - - .. py:attribute:: user_agent_context - :type: str - - -.. py:class:: DataverseConfig - - Configuration settings for Dataverse client operations. - - :param language_code: LCID (Locale ID) for localized labels and messages. Default is 1033 (English - United States). - :type language_code: :class:`int` - :param http_retries: Optional maximum number of retry attempts for transient HTTP errors. Reserved for future use. - :type http_retries: :class:`int` or None - :param http_backoff: Optional backoff multiplier (in seconds) between retry attempts. Reserved for future use. - :type http_backoff: :class:`float` or None - :param http_timeout: Optional request timeout in seconds. Reserved for future use. - :type http_timeout: :class:`float` or None - :param log_config: Optional local HTTP diagnostics logging configuration. - When provided, all HTTP requests and responses are logged to timestamped - ``.log`` files with automatic redaction of sensitive headers. - :type log_config: ~PowerPlatform.Dataverse.core.log_config.LogConfig or None - :param operation_context: Optional caller-defined context object appended to the - outbound ``User-Agent`` header as a parenthesized comment. Intended for - plugin/tool attribution. - :type operation_context: ~PowerPlatform.Dataverse.core.config.OperationContext or None - - - .. py:attribute:: language_code - :type: int - :value: 1033 - - - - .. py:attribute:: http_retries - :type: Optional[int] - :value: None - - - - .. py:attribute:: http_backoff - :type: Optional[float] - :value: None - - - - .. py:attribute:: http_timeout - :type: Optional[float] - :value: None - - - - .. py:attribute:: log_config - :type: Optional[PowerPlatform.Dataverse.core.log_config.LogConfig] - :value: None - - - - .. py:attribute:: operation_context - :type: Optional[OperationContext] - :value: None - - - - .. py:method:: from_env() -> DataverseConfig - :classmethod: - - - Create a configuration instance with default settings. - - :return: Configuration instance with default values. - :rtype: ~PowerPlatform.Dataverse.core.config.DataverseConfig - - - diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/core/errors/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/core/errors/index.rst.txt deleted file mode 100644 index 7fb487a0..00000000 --- a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/core/errors/index.rst.txt +++ /dev/null @@ -1,185 +0,0 @@ -PowerPlatform.Dataverse.core.errors -=================================== - -.. py:module:: PowerPlatform.Dataverse.core.errors - -.. autoapi-nested-parse:: - - Structured Dataverse exception hierarchy. - - This module provides :class:`~PowerPlatform.Dataverse.core.errors.DataverseError` and - specialized :class:`~PowerPlatform.Dataverse.core.errors.ValidationError`, - :class:`~PowerPlatform.Dataverse.core.errors.MetadataError`, - :class:`~PowerPlatform.Dataverse.core.errors.SQLParseError`, and - :class:`~PowerPlatform.Dataverse.core.errors.HttpError` for validation, metadata, - SQL parsing, and Web API HTTP failures. - - - -Exceptions ----------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.core.errors.DataverseError - PowerPlatform.Dataverse.core.errors.ValidationError - PowerPlatform.Dataverse.core.errors.MetadataError - PowerPlatform.Dataverse.core.errors.SQLParseError - PowerPlatform.Dataverse.core.errors.HttpError - - -Module Contents ---------------- - -.. py:exception:: DataverseError(message: str, code: str, subcode: Optional[str] = None, status_code: Optional[int] = None, details: Optional[Dict[str, Any]] = None, source: Optional[str] = None, is_transient: bool = False) - - Bases: :py:obj:`Exception` - - - Base structured exception for the Dataverse SDK. - - :param message: Human-readable error message. - :type message: :class:`str` - :param code: Error category code (e.g. ``"validation_error"``, ``"http_error"``). - :type code: :class:`str` - :param subcode: Optional subcategory or specific error identifier. - :type subcode: :class:`str` | None - :param status_code: Optional HTTP status code if the error originated from an HTTP response. - :type status_code: :class:`int` | None - :param details: Optional dictionary containing additional diagnostic information. - :type details: :class:`dict` | None - :param source: Error source, either ``"client"`` or ``"server"``. - :type source: :class:`str` - :param is_transient: Whether the error is potentially transient and may succeed on retry. - :type is_transient: :class:`bool` - - Initialize self. See help(type(self)) for accurate signature. - - - .. py:attribute:: message - - - .. py:attribute:: code - - - .. py:attribute:: subcode - :value: None - - - - .. py:attribute:: status_code - :value: None - - - - .. py:attribute:: details - - - .. py:attribute:: source - :value: 'client' - - - - .. py:attribute:: is_transient - :value: False - - - - .. py:attribute:: timestamp - - - .. py:method:: to_dict() -> Dict[str, Any] - - Convert the error to a dictionary representation. - - :return: Dictionary containing all error properties. - :rtype: :class:`dict` - - - -.. py:exception:: ValidationError(message: str, *, subcode: Optional[str] = None, details: Optional[Dict[str, Any]] = None) - - Bases: :py:obj:`DataverseError` - - - Exception raised for client-side validation failures. - - :param message: Human-readable validation error message. - :type message: :class:`str` - :param subcode: Optional specific validation error identifier. - :type subcode: :class:`str` | None - :param details: Optional dictionary with additional validation context. - :type details: :class:`dict` | None - - Initialize self. See help(type(self)) for accurate signature. - - -.. py:exception:: MetadataError(message: str, *, subcode: Optional[str] = None, details: Optional[Dict[str, Any]] = None) - - Bases: :py:obj:`DataverseError` - - - Exception raised for metadata operation failures. - - :param message: Human-readable metadata error message. - :type message: :class:`str` - :param subcode: Optional specific metadata error identifier. - :type subcode: :class:`str` | None - :param details: Optional dictionary with additional metadata context. - :type details: :class:`dict` | None - - Initialize self. See help(type(self)) for accurate signature. - - -.. py:exception:: SQLParseError(message: str, *, subcode: Optional[str] = None, details: Optional[Dict[str, Any]] = None) - - Bases: :py:obj:`DataverseError` - - - Exception raised for SQL query parsing failures. - - :param message: Human-readable SQL parsing error message. - :type message: :class:`str` - :param subcode: Optional specific SQL parsing error identifier. - :type subcode: :class:`str` | None - :param details: Optional dictionary with SQL query context and parse information. - :type details: :class:`dict` | None - - Initialize self. See help(type(self)) for accurate signature. - - -.. py:exception:: HttpError(message: str, status_code: int, is_transient: bool = False, subcode: Optional[str] = None, service_error_code: Optional[str] = None, correlation_id: Optional[str] = None, client_request_id: Optional[str] = None, service_request_id: Optional[str] = None, traceparent: Optional[str] = None, body_excerpt: Optional[str] = None, retry_after: Optional[int] = None, details: Optional[Dict[str, Any]] = None) - - Bases: :py:obj:`DataverseError` - - - Exception raised for HTTP request failures from the Dataverse Web API. - - :param message: Human-readable HTTP error message, typically from the API error response. - :type message: :class:`str` - :param status_code: HTTP status code (e.g. 400, 404, 500). - :type status_code: :class:`int` - :param is_transient: Whether the error is transient (429, 503, 504) and may succeed on retry. - :type is_transient: :class:`bool` - :param subcode: Optional HTTP status category (e.g. ``"4xx"``, ``"5xx"``). - :type subcode: :class:`str` | None - :param service_error_code: Optional Dataverse-specific error code from the API response. - :type service_error_code: :class:`str` | None - :param correlation_id: Optional client-generated correlation ID for tracking requests within an SDK call. - :type correlation_id: :class:`str` | None - :param client_request_id: Optional client-generated request ID injected into outbound headers. - :type client_request_id: :class:`str` | None - :param service_request_id: Optional ``x-ms-service-request-id`` value returned by Dataverse servers. - :type service_request_id: :class:`str` | None - :param traceparent: Optional W3C trace context for distributed tracing. - :type traceparent: :class:`str` | None - :param body_excerpt: Optional excerpt of the response body for diagnostics. - :type body_excerpt: :class:`str` | None - :param retry_after: Optional number of seconds to wait before retrying (from Retry-After header). - :type retry_after: :class:`int` | None - :param details: Optional additional diagnostic details. - :type details: :class:`dict` | None - - Initialize self. See help(type(self)) for accurate signature. - - diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/core/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/core/index.rst.txt deleted file mode 100644 index 31a27202..00000000 --- a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/core/index.rst.txt +++ /dev/null @@ -1,25 +0,0 @@ -PowerPlatform.Dataverse.core -============================ - -.. py:module:: PowerPlatform.Dataverse.core - -.. autoapi-nested-parse:: - - Core infrastructure components for the Dataverse SDK. - - This module contains the foundational components including authentication, - configuration, HTTP client, and error handling. - - - -Submodules ----------- - -.. toctree:: - :maxdepth: 1 - - /autoapi/PowerPlatform/Dataverse/core/config/index - /autoapi/PowerPlatform/Dataverse/core/errors/index - /autoapi/PowerPlatform/Dataverse/core/log_config/index - - diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/core/log_config/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/core/log_config/index.rst.txt deleted file mode 100644 index aa4a5e09..00000000 --- a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/core/log_config/index.rst.txt +++ /dev/null @@ -1,90 +0,0 @@ -PowerPlatform.Dataverse.core.log_config -======================================= - -.. py:module:: PowerPlatform.Dataverse.core.log_config - -.. autoapi-nested-parse:: - - Local file logging configuration for Dataverse SDK HTTP diagnostics. - - Provides :class:`~PowerPlatform.Dataverse.core.log_config.LogConfig`, an opt-in configuration for writing request/response - traces to ``.log`` files with automatic header redaction and timestamped filenames. - - - -Classes -------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.core.log_config.LogConfig - - -Module Contents ---------------- - -.. py:class:: LogConfig - - Configuration for local HTTP diagnostics logging. - - When provided to :class:`~PowerPlatform.Dataverse.client.DataverseClient` via - :class:`~PowerPlatform.Dataverse.core.config.DataverseConfig`, every HTTP request - and response is logged to timestamped ``.log`` files in the specified folder. - Sensitive headers (e.g. ``Authorization``) are automatically redacted. - - :param log_folder: Directory path for log files. Created automatically if missing. - Default: ``"./dataverse_logs"`` - :param log_file_prefix: Filename prefix. Timestamp is appended automatically. - Default: ``"dataverse"`` → ``dataverse_20260310_143022.log`` - :param max_body_bytes: Maximum bytes of request/response body to capture. - ``0`` (default) disables body capture. Enable only for active debugging - sessions — bodies may contain PII and sensitive business data. - :param redacted_headers: Header names (case-insensitive) whose values are - replaced with ``"[REDACTED]"`` in logs. Defaults include - ``Authorization``, ``Proxy-Authorization``, etc. - :param log_level: Python logging level name. Default: ``"DEBUG"``. - :param max_file_bytes: Max size per log file before rotation (bytes). - Default: ``10_485_760`` (10 MB). - :param backup_count: Number of rotated backup files to keep. Default: ``5``. - - - .. py:attribute:: log_folder - :type: str - :value: './dataverse_logs' - - - - .. py:attribute:: log_file_prefix - :type: str - :value: 'dataverse' - - - - .. py:attribute:: max_body_bytes - :type: int - :value: 0 - - - - .. py:attribute:: redacted_headers - :type: FrozenSet[str] - - - .. py:attribute:: log_level - :type: str - :value: 'DEBUG' - - - - .. py:attribute:: max_file_bytes - :type: int - :value: 10485760 - - - - .. py:attribute:: backup_count - :type: int - :value: 5 - - - diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/data/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/data/index.rst.txt deleted file mode 100644 index 62e70f16..00000000 --- a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/data/index.rst.txt +++ /dev/null @@ -1,14 +0,0 @@ -PowerPlatform.Dataverse.data -============================ - -.. py:module:: PowerPlatform.Dataverse.data - -.. autoapi-nested-parse:: - - Data access layer for the Dataverse SDK. - - This module contains OData protocol handling, CRUD operations, metadata management, - SQL query functionality, and file upload capabilities. - - - diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/extensions/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/extensions/index.rst.txt deleted file mode 100644 index 4f88c87c..00000000 --- a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/extensions/index.rst.txt +++ /dev/null @@ -1,11 +0,0 @@ -PowerPlatform.Dataverse.extensions -================================== - -.. py:module:: PowerPlatform.Dataverse.extensions - -.. autoapi-nested-parse:: - - Optional extensions for the Dataverse SDK. Currently a placeholder. - - - diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/index.rst.txt deleted file mode 100644 index 8f38b9f8..00000000 --- a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/index.rst.txt +++ /dev/null @@ -1,24 +0,0 @@ -PowerPlatform.Dataverse -======================= - -.. py:module:: PowerPlatform.Dataverse - - -Submodules ----------- - -.. toctree:: - :maxdepth: 1 - - /autoapi/PowerPlatform/Dataverse/claude_skill/index - /autoapi/PowerPlatform/Dataverse/client/index - /autoapi/PowerPlatform/Dataverse/common/index - /autoapi/PowerPlatform/Dataverse/core/index - /autoapi/PowerPlatform/Dataverse/data/index - /autoapi/PowerPlatform/Dataverse/extensions/index - /autoapi/PowerPlatform/Dataverse/migration/index - /autoapi/PowerPlatform/Dataverse/models/index - /autoapi/PowerPlatform/Dataverse/operations/index - /autoapi/PowerPlatform/Dataverse/utils/index - - diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/migration/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/migration/index.rst.txt deleted file mode 100644 index 15bc1e31..00000000 --- a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/migration/index.rst.txt +++ /dev/null @@ -1,15 +0,0 @@ -PowerPlatform.Dataverse.migration -================================= - -.. py:module:: PowerPlatform.Dataverse.migration - - -Submodules ----------- - -.. toctree:: - :maxdepth: 1 - - /autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index - - diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index.rst.txt deleted file mode 100644 index 0e6e73c2..00000000 --- a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index.rst.txt +++ /dev/null @@ -1,119 +0,0 @@ -PowerPlatform.Dataverse.migration.migrate_v0_to_v1 -================================================== - -.. py:module:: PowerPlatform.Dataverse.migration.migrate_v0_to_v1 - -.. autoapi-nested-parse:: - - DV-Python-SDK v0 -> v1 GA migration codemod. - - Mechanically rewrites beta (0.1.0b*) call sites to their GA (1.0) equivalents - using LibCST (concrete syntax tree — preserves all whitespace and comments). - - Usage:: - - pip install PowerPlatform-Dataverse-Client[migration] - dataverse-migrate path/to/your/scripts/ - dataverse-migrate path/to/your/scripts/ --dry-run # preview without writing - dataverse-migrate path/to/your/scripts/ --client-var=svc # if client is named 'svc' - - # Or via module for development installs: - python -m PowerPlatform.Dataverse.migration.migrate_v0_to_v1 path/to/your/scripts/ - - Transformations applied - ----------------------- - Builder methods (.filter_* -> .where(col(...)...)):: - - .filter_eq("col", v) -> .where(col("col") == v) - .filter_ne("col", v) -> .where(col("col") != v) - .filter_gt("col", v) -> .where(col("col") > v) - .filter_ge("col", v) -> .where(col("col") >= v) - .filter_lt("col", v) -> .where(col("col") < v) - .filter_le("col", v) -> .where(col("col") <= v) - .filter_contains("col", v) -> .where(col("col").contains(v)) - .filter_startswith("col", v) -> .where(col("col").startswith(v)) - .filter_endswith("col", v) -> .where(col("col").endswith(v)) - .filter_in("col", vals) -> .where(col("col").in_(vals)) - .filter_not_in("col", vals) -> .where(col("col").not_in(vals)) - .filter_null("col") -> .where(col("col").is_null()) - .filter_not_null("col") -> .where(col("col").is_not_null()) - .filter_between("col", lo, hi) -> .where(col("col").between(lo, hi)) - .filter_not_between("col", lo, hi) -> .where(col("col").not_between(lo, hi)) - .filter_raw("expr") -> .where(raw("expr")) - .filter("expr") -> .where(raw("expr")) - .execute(by_page=True) -> .execute_pages() - .execute(by_page=False) -> .execute() (flag removed) - .to_dataframe() -> .execute().to_dataframe() - Inserts .execute() when the receiver is a recognised QueryBuilder chain - (contains .builder(), .select(), .where(), or a .filter_*() call). - - Record namespace:: - - batch.records.get(t, id) -> batch.records.retrieve(t, id) - - Top-level shortcuts (removed at GA):: - - client.create(t, d) -> client.records.create(t, d) - client.update(t, id, d) -> client.records.update(t, id, d) - client.delete(t, id) -> client.records.delete(t, id) - client.get(t, id) -> client.records.get(t, id) [deprecated; see manual section] - client.query_sql(sql) -> client.query.sql(sql) - client.get_table_info(t) -> client.tables.get(t) - client.create_table(t, …) -> client.tables.create(t, …) - client.delete_table(t) -> client.tables.delete(t) - client.list_tables() -> client.tables.list() - client.create_columns(t, …) -> client.tables.add_columns(t, …) - client.delete_columns(t, …) -> client.tables.remove_columns(t, …) - client.upload_file(…) -> client.files.upload(…) - - Import management: - Adds ``from PowerPlatform.Dataverse.models.filters import col`` when a - .filter_* method is rewritten (if col is not already imported). - Adds ``raw`` to the same import when .filter_raw or .filter is rewritten. - - NOT handled by this codemod (manual migration required): - execute(by_page=variable) -> manual review required (variable argument, not literal) - client.records.get(t, id) -> client.records.retrieve(t, id) - Return type changes: beta returns Record (raises on 404); GA retrieve() returns - Record | None. Callers that do not guard against None will fail silently. - client.records.get(t, kw=…) -> client.records.list(t, kw=…) - Return type changes: beta returns Iterable[List[Record]] (pages); GA list() - returns QueryResult (flat iterable over Records). Any ``for page in result: - for rec in page:`` iteration pattern breaks after a mechanical rename. - client.dataframe.get() -> client.query.builder(…).execute().to_dataframe() - Expression reconstruction requires understanding caller intent. - client.query.sql_select()/sql_join()/sql_joins() -> removed (no mechanical replacement) - - - -Functions ---------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.migration.migrate_v0_to_v1.find_manual_patterns - PowerPlatform.Dataverse.migration.migrate_v0_to_v1.migrate_source - PowerPlatform.Dataverse.migration.migrate_v0_to_v1.migrate_file - PowerPlatform.Dataverse.migration.migrate_v0_to_v1.main - - -Module Contents ---------------- - -.. py:function:: find_manual_patterns(source: str, *, client_var: str = 'client') -> List[str] - - Return descriptions of patterns in *source* that require manual migration. - - -.. py:function:: migrate_source(source: str, *, client_var: str = 'client') -> str - - Parse *source*, apply transformations, return migrated source. - - -.. py:function:: migrate_file(path: pathlib.Path, *, dry_run: bool = False, client_var: str = 'client') -> Tuple[bool, List[str]] - - Migrate *path* in place. Returns (was_changed, manual_review_notes). - - -.. py:function:: main(argv: Optional[List[str]] = None) -> int - diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/batch/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/batch/index.rst.txt deleted file mode 100644 index a0d3d118..00000000 --- a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/batch/index.rst.txt +++ /dev/null @@ -1,154 +0,0 @@ -PowerPlatform.Dataverse.models.batch -==================================== - -.. py:module:: PowerPlatform.Dataverse.models.batch - -.. autoapi-nested-parse:: - - Public result types for batch operations. - - - -Classes -------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.models.batch.BatchItemResponse - PowerPlatform.Dataverse.models.batch.BatchResult - - -Module Contents ---------------- - -.. py:class:: BatchItemResponse - - Response from a single operation within a batch request. - - Responses are returned in submission order. For operations added to a - changeset, responses appear in the changeset's position in that order. - - :param status_code: HTTP status code for this operation (e.g. 204, 200, 400). - :param content_id: ``Content-ID`` value from the changeset response part, if any. - :param entity_id: GUID extracted from the ``OData-EntityId`` response header. - Set for successful create (POST) operations. - :param data: Parsed JSON response body (e.g. for GET operations). - :param error_message: Error message when the operation failed. - :param error_code: Service error code when the operation failed. - - Example:: - - for item in result.responses: - if item.is_success: - print(f"[OK] {item.status_code} entity_id={item.entity_id}") - else: - print(f"[ERR] {item.status_code}: {item.error_message}") - - - .. py:attribute:: status_code - :type: int - - - .. py:attribute:: content_id - :type: Optional[str] - :value: None - - - - .. py:attribute:: entity_id - :type: Optional[str] - :value: None - - - - .. py:attribute:: data - :type: Optional[Dict[str, Any]] - :value: None - - - - .. py:attribute:: error_message - :type: Optional[str] - :value: None - - - - .. py:attribute:: error_code - :type: Optional[str] - :value: None - - - - .. py:property:: is_success - :type: bool - - - Return True when status_code is 2xx. - - -.. py:class:: BatchResult - - Result of executing a batch request. - - Contains one :class:`BatchItemResponse` per HTTP operation submitted. - Operations that expand to multiple HTTP requests (e.g. ``add_columns`` - with three columns) contribute three entries. - - :param responses: All responses in submission order. - - Example:: - - result = client.batch.new().execute() - print(f"Succeeded: {len(result.succeeded)}, Failed: {len(result.failed)}") - for guid in result.entity_ids: - print(f"[OK] entity_id: {guid}") - - - .. py:attribute:: responses - :type: List[BatchItemResponse] - :value: [] - - - - .. py:property:: succeeded - :type: List[BatchItemResponse] - - - Responses with 2xx status codes. - - - .. py:property:: failed - :type: List[BatchItemResponse] - - - Responses with non-2xx status codes. - - - .. py:property:: has_errors - :type: bool - - - True when any response has a non-2xx status code. - - - .. py:property:: entity_ids - :type: List[str] - - - GUIDs extracted from ``OData-EntityId`` headers of successful responses. - - Returns entity IDs from any successful (2xx) response that includes an - ``OData-EntityId`` header. Both individual ``POST`` (create) and - ``PATCH`` (update) operations return this header with the record's GUID. - ``GET`` and ``DELETE`` operations do not. - - .. note:: - ``CreateMultiple`` and ``UpsertMultiple`` action responses do **not** - return per-record ``OData-EntityId`` headers. Their IDs are in the - JSON response body (``data["Ids"]``). Access them via:: - - for resp in result.succeeded: - if resp.data and "Ids" in resp.data: - bulk_ids = resp.data["Ids"] - - diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index.rst.txt deleted file mode 100644 index 1632cc4a..00000000 --- a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index.rst.txt +++ /dev/null @@ -1,76 +0,0 @@ -PowerPlatform.Dataverse.models.fetchxml_query -============================================= - -.. py:module:: PowerPlatform.Dataverse.models.fetchxml_query - -.. autoapi-nested-parse:: - - FetchXmlQuery — inert query object returned by QueryOperations.fetchxml(). - - - -Classes -------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.models.fetchxml_query.FetchXmlQuery - - -Module Contents ---------------- - -.. py:class:: FetchXmlQuery(xml: str, entity_name: str, client: PowerPlatform.Dataverse.client.DataverseClient) - - Inert FetchXML query object. No HTTP request is made until - :meth:`execute` or :meth:`execute_pages` is called. - - Obtained via ``client.query.fetchxml(xml)``. - - :param xml: Stripped, well-formed FetchXML string. - :param entity_name: Entity schema name from the ```` element. - :param client: Parent :class:`~PowerPlatform.Dataverse.client.DataverseClient`. - - - .. py:method:: execute() -> PowerPlatform.Dataverse.models.record.QueryResult - - Execute the FetchXML query and return all results as a :class:`~PowerPlatform.Dataverse.models.record.QueryResult`. - - Blocking — fetches all pages upfront and holds every record in memory before - returning. Simple for small-to-medium result sets; use :meth:`execute_pages` - when the result set may be large or you want to process records as they arrive. - - :return: All matching records across all pages. - :rtype: :class:`~PowerPlatform.Dataverse.models.record.QueryResult` - - Example:: - - rows = client.query.fetchxml(xml).execute() - df = rows.to_dataframe() - - - - .. py:method:: execute_pages() -> Iterator[PowerPlatform.Dataverse.models.record.QueryResult] - - Lazily yield one :class:`~PowerPlatform.Dataverse.models.record.QueryResult` per HTTP page. - - Streaming — each iteration fires one HTTP request and yields one page. - Prefer over :meth:`execute` when: - - - The result set may be large and you do not want all records in memory at once. - - You want early exit: stop iterating once you find what you need and the - remaining HTTP round-trips are skipped automatically. - - You need per-page progress reporting or batched downstream writes. - - One-shot — do not iterate more than once. - - :return: Iterator of per-page :class:`~PowerPlatform.Dataverse.models.record.QueryResult` objects. - :rtype: Iterator[:class:`~PowerPlatform.Dataverse.models.record.QueryResult`] - - Example:: - - for page in client.query.fetchxml(xml).execute_pages(): - process(page.to_dataframe()) - - - diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/filters/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/filters/index.rst.txt deleted file mode 100644 index 1e85b977..00000000 --- a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/filters/index.rst.txt +++ /dev/null @@ -1,365 +0,0 @@ -PowerPlatform.Dataverse.models.filters -====================================== - -.. py:module:: PowerPlatform.Dataverse.models.filters - -.. autoapi-nested-parse:: - - Composable OData filter expressions for the Dataverse SDK. - - Provides an expression tree that compiles to OData ``$filter`` strings, - with Python operator overloads (``&``, ``|``, ``~``) for composing - complex filter conditions. - - Example:: - - from PowerPlatform.Dataverse.models.filters import col, raw - - # Preferred GA idiom — col() proxy - expr = col("statecode") == 0 - print(expr.to_odata()) # statecode eq 0 - - # Complex composition with OR and AND - expr = (col("statecode") == 0) | (col("statecode") == 1) & (col("revenue") > 100000) - print(expr.to_odata()) - - # In / not-in - expr = col("statecode").in_([0, 1, 2]) - print(expr.to_odata()) - # Microsoft.Dynamics.CRM.In(PropertyName='statecode',PropertyValues=["0","1","2"]) - - # Raw OData escape hatch (no deprecation warning) - expr = raw("Microsoft.Dynamics.CRM.Today(PropertyName='createdon')") - - # Negation - expr = ~(col("statecode") == 1) - print(expr.to_odata()) # not (statecode eq 1) - - - -Classes -------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.models.filters.FilterExpression - PowerPlatform.Dataverse.models.filters.ColumnProxy - - -Functions ---------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.models.filters.col - PowerPlatform.Dataverse.models.filters.raw - PowerPlatform.Dataverse.models.filters.eq - PowerPlatform.Dataverse.models.filters.ne - PowerPlatform.Dataverse.models.filters.gt - PowerPlatform.Dataverse.models.filters.ge - PowerPlatform.Dataverse.models.filters.lt - PowerPlatform.Dataverse.models.filters.le - PowerPlatform.Dataverse.models.filters.contains - PowerPlatform.Dataverse.models.filters.startswith - PowerPlatform.Dataverse.models.filters.endswith - PowerPlatform.Dataverse.models.filters.between - PowerPlatform.Dataverse.models.filters.is_null - PowerPlatform.Dataverse.models.filters.is_not_null - PowerPlatform.Dataverse.models.filters.filter_in - PowerPlatform.Dataverse.models.filters.not_in - PowerPlatform.Dataverse.models.filters.not_between - - -Module Contents ---------------- - -.. py:class:: FilterExpression - - Base class for composable OData filter expressions. - - Supports Python operator overloads for logical composition: - - - ``expr1 & expr2`` produces ``(expr1 and expr2)`` - - ``expr1 | expr2`` produces ``(expr1 or expr2)`` - - ``~expr`` produces ``not (expr)`` - - - .. py:method:: to_odata() -> str - :abstractmethod: - - - Compile this expression to an OData ``$filter`` string. - - - -.. py:class:: ColumnProxy(name: str) - - Fluent proxy for building OData filter expressions from a column name. - - Returned by :func:`col`. Operator overloads and methods produce - :class:`FilterExpression` instances that can be passed to - ``QueryBuilder.where()``. - - Example:: - - from PowerPlatform.Dataverse.models.filters import col - - expr = col("statecode") == 0 # equality - expr = col("revenue") > 1_000_000 # comparison - expr = col("name").like("Contoso%") # startswith - expr = col("name").is_null() # null check - expr = col("statecode").in_([0, 1]) # in - - - .. py:method:: is_null() -> FilterExpression - - Column equals null: ``column eq null``. - - - - .. py:method:: is_not_null() -> FilterExpression - - Column not null: ``column ne null``. - - - - .. py:method:: in_(values: Collection[Any]) -> FilterExpression - - In filter using ``Microsoft.Dynamics.CRM.In``. - - :param values: Non-empty collection of values. - :raises ValueError: If ``values`` is empty. - - - - .. py:method:: not_in(values: Collection[Any]) -> FilterExpression - - Not-in filter using ``Microsoft.Dynamics.CRM.NotIn``. - - :param values: Non-empty collection of values. - :raises ValueError: If ``values`` is empty. - - - - .. py:method:: between(lo: Any, hi: Any) -> FilterExpression - - Between filter: ``(column ge lo and column le hi)``. - - - - .. py:method:: not_between(lo: Any, hi: Any) -> FilterExpression - - Not-between filter: ``not (column ge lo and column le hi)``. - - - - .. py:method:: contains(value: str) -> FilterExpression - - Contains filter: ``contains(column, value)``. - - - - .. py:method:: startswith(value: str) -> FilterExpression - - Startswith filter: ``startswith(column, value)``. - - - - .. py:method:: endswith(value: str) -> FilterExpression - - Endswith filter: ``endswith(column, value)``. - - - - .. py:method:: like(pattern: str) -> FilterExpression - - Pattern-match filter compiled to the closest OData equivalent. - - +-----------------+-----------------------------+-------------------------------------+ - | Pattern form | Example | Compiles to | - +=================+=============================+=====================================+ - | ``val%`` | ``like("Contoso%")`` | ``startswith(column,'Contoso')`` | - +-----------------+-----------------------------+-------------------------------------+ - | ``%val`` | ``like("%Ltd")`` | ``endswith(column,'Ltd')`` | - +-----------------+-----------------------------+-------------------------------------+ - | ``%val%`` | ``like("%Corp%")`` | ``contains(column,'Corp')`` | - +-----------------+-----------------------------+-------------------------------------+ - | No wildcard | ``like("Contoso")`` | ``column eq 'Contoso'`` | - +-----------------+-----------------------------+-------------------------------------+ - | Other | ``like("Con%oso")`` | :class:`ValueError` | - +-----------------+-----------------------------+-------------------------------------+ - - :param pattern: LIKE-style pattern string. - :raises ValueError: If the pattern cannot be reduced to a single OData function. - - - - .. py:method:: not_like(pattern: str) -> FilterExpression - - Negated pattern-match filter; mirrors :meth:`like` rules then negates. - - :param pattern: LIKE-style pattern string (same rules as :meth:`like`). - :raises ValueError: If the pattern cannot be reduced to a single OData function. - - - -.. py:function:: col(name: str) -> ColumnProxy - - Return a :class:`ColumnProxy` for building filter expressions. - - This is the preferred GA idiom for constructing filter expressions:: - - from PowerPlatform.Dataverse.models.filters import col - - expr = col("statecode") == 0 - expr = col("revenue") > 1_000_000 - expr = col("name").like("Contoso%") - expr = col("statecode").in_([0, 1]) - expr = col("parentaccountid").is_null() - - :param name: Column logical name (case-insensitive, will be lowercased). - :return: A :class:`ColumnProxy` bound to the column. - :raises ValueError: If ``name`` is empty. - - -.. py:function:: raw(filter_string: str) -> FilterExpression - - Verbatim OData filter expression (passed through unchanged). - - This function is **not** deprecated — it is the OData escape hatch with - no typed replacement. - - :param filter_string: Raw OData filter string. - :return: A :class:`FilterExpression`. - - Example:: - - raw("Microsoft.Dynamics.CRM.Today(PropertyName='createdon')") - - -.. py:function:: eq(column: str, value: Any) -> FilterExpression - - Equality filter: ``column eq value``. - - .. deprecated:: - Use ``col(column) == value`` instead. - - -.. py:function:: ne(column: str, value: Any) -> FilterExpression - - Not-equal filter: ``column ne value``. - - .. deprecated:: - Use ``col(column) != value`` instead. - - -.. py:function:: gt(column: str, value: Any) -> FilterExpression - - Greater-than filter: ``column gt value``. - - .. deprecated:: - Use ``col(column) > value`` instead. - - -.. py:function:: ge(column: str, value: Any) -> FilterExpression - - Greater-than-or-equal filter: ``column ge value``. - - .. deprecated:: - Use ``col(column) >= value`` instead. - - -.. py:function:: lt(column: str, value: Any) -> FilterExpression - - Less-than filter: ``column lt value``. - - .. deprecated:: - Use ``col(column) < value`` instead. - - -.. py:function:: le(column: str, value: Any) -> FilterExpression - - Less-than-or-equal filter: ``column le value``. - - .. deprecated:: - Use ``col(column) <= value`` instead. - - -.. py:function:: contains(column: str, value: str) -> FilterExpression - - Contains filter: ``contains(column, value)``. - - .. deprecated:: - Use ``col(column).contains(value)`` instead. - - -.. py:function:: startswith(column: str, value: str) -> FilterExpression - - Startswith filter: ``startswith(column, value)``. - - .. deprecated:: - Use ``col(column).startswith(value)`` instead. - - -.. py:function:: endswith(column: str, value: str) -> FilterExpression - - Endswith filter: ``endswith(column, value)``. - - .. deprecated:: - Use ``col(column).endswith(value)`` instead. - - -.. py:function:: between(column: str, low: Any, high: Any) -> FilterExpression - - Between filter: ``(column ge low and column le high)``. - - .. deprecated:: - Use ``col(column).between(low, high)`` instead. - - -.. py:function:: is_null(column: str) -> FilterExpression - - Null check: ``column eq null``. - - .. deprecated:: - Use ``col(column).is_null()`` instead. - - -.. py:function:: is_not_null(column: str) -> FilterExpression - - Not-null check: ``column ne null``. - - .. deprecated:: - Use ``col(column).is_not_null()`` instead. - - -.. py:function:: filter_in(column: str, values: Collection[Any]) -> FilterExpression - - In filter using ``Microsoft.Dynamics.CRM.In``. - - Named ``filter_in`` because ``in`` is a Python keyword. - - .. deprecated:: - Use ``col(column).in_(values)`` instead. - - :raises ValueError: If ``values`` is empty. - - -.. py:function:: not_in(column: str, values: Collection[Any]) -> FilterExpression - - Not-in filter using ``Microsoft.Dynamics.CRM.NotIn``. - - .. deprecated:: - Use ``col(column).not_in(values)`` instead. - - :raises ValueError: If ``values`` is empty. - - -.. py:function:: not_between(column: str, low: Any, high: Any) -> FilterExpression - - Not-between filter: ``not (column ge low and column le high)``. - - .. deprecated:: - Use ``col(column).not_between(low, high)`` instead. - - diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/index.rst.txt deleted file mode 100644 index b7ae9520..00000000 --- a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/index.rst.txt +++ /dev/null @@ -1,41 +0,0 @@ -PowerPlatform.Dataverse.models -============================== - -.. py:module:: PowerPlatform.Dataverse.models - -.. autoapi-nested-parse:: - - Data models and type definitions for the Dataverse SDK. - - Provides dataclasses and helpers for Dataverse entities: - - - :class:`~PowerPlatform.Dataverse.models.query_builder.QueryBuilder`: Fluent query builder. - - :mod:`~PowerPlatform.Dataverse.models.filters`: Composable OData filter expressions - via :func:`~PowerPlatform.Dataverse.models.filters.col` and - :func:`~PowerPlatform.Dataverse.models.filters.raw`. - - :class:`~PowerPlatform.Dataverse.models.record.QueryResult`: Iterable result wrapper. - - :class:`~PowerPlatform.Dataverse.models.record.Record`: Dataverse entity record. - - :class:`~PowerPlatform.Dataverse.models.upsert.UpsertItem`: Upsert operation item. - - :class:`~PowerPlatform.Dataverse.models.fetchxml_query.FetchXmlQuery`: FetchXML query object. - - :class:`~PowerPlatform.Dataverse.models.protocol.DataverseModel`: Typed-model protocol. - - - -Submodules ----------- - -.. toctree:: - :maxdepth: 1 - - /autoapi/PowerPlatform/Dataverse/models/batch/index - /autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index - /autoapi/PowerPlatform/Dataverse/models/filters/index - /autoapi/PowerPlatform/Dataverse/models/labels/index - /autoapi/PowerPlatform/Dataverse/models/protocol/index - /autoapi/PowerPlatform/Dataverse/models/query_builder/index - /autoapi/PowerPlatform/Dataverse/models/record/index - /autoapi/PowerPlatform/Dataverse/models/relationship/index - /autoapi/PowerPlatform/Dataverse/models/table_info/index - /autoapi/PowerPlatform/Dataverse/models/upsert/index - - diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/labels/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/labels/index.rst.txt deleted file mode 100644 index f2df2a89..00000000 --- a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/labels/index.rst.txt +++ /dev/null @@ -1,113 +0,0 @@ -PowerPlatform.Dataverse.models.labels -===================================== - -.. py:module:: PowerPlatform.Dataverse.models.labels - -.. autoapi-nested-parse:: - - Label models for Dataverse metadata. - - - -Classes -------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.models.labels.LocalizedLabel - PowerPlatform.Dataverse.models.labels.Label - - -Module Contents ---------------- - -.. py:class:: LocalizedLabel - - Represents a localized label with a language code. - - :param label: The text of the label. - :type label: str - :param language_code: The language code (LCID), e.g., 1033 for English. - :type language_code: int - :param additional_properties: Optional dict of additional properties to include - in the Web API payload. These are merged last and can override default values. - :type additional_properties: Optional[Dict[str, Any]] - - - .. py:attribute:: label - :type: str - - - .. py:attribute:: language_code - :type: int - - - .. py:attribute:: additional_properties - :type: Optional[Dict[str, Any]] - :value: None - - - - .. py:method:: to_dict() -> Dict[str, Any] - - Convert to Web API JSON format. - - Example:: - - >>> label = LocalizedLabel(label="Account", language_code=1033) - >>> label.to_dict() - { - '@odata.type': 'Microsoft.Dynamics.CRM.LocalizedLabel', - 'Label': 'Account', - 'LanguageCode': 1033 - } - - - -.. py:class:: Label - - Represents a label that can have multiple localized versions. - - :param localized_labels: List of LocalizedLabel instances. - :type localized_labels: List[LocalizedLabel] - :param user_localized_label: Optional user-specific localized label. - :type user_localized_label: Optional[LocalizedLabel] - :param additional_properties: Optional dict of additional properties to include - in the Web API payload. These are merged last and can override default values. - :type additional_properties: Optional[Dict[str, Any]] - - - .. py:attribute:: localized_labels - :type: List[LocalizedLabel] - - - .. py:attribute:: user_localized_label - :type: Optional[LocalizedLabel] - :value: None - - - - .. py:attribute:: additional_properties - :type: Optional[Dict[str, Any]] - :value: None - - - - .. py:method:: to_dict() -> Dict[str, Any] - - Convert to Web API JSON format. - - Example:: - - >>> label = Label(localized_labels=[LocalizedLabel("Account", 1033)]) - >>> label.to_dict() - { - '@odata.type': 'Microsoft.Dynamics.CRM.Label', - 'LocalizedLabels': [ - {'@odata.type': '...', 'Label': 'Account', 'LanguageCode': 1033} - ], - 'UserLocalizedLabel': {'@odata.type': '...', 'Label': 'Account', ...} - } - - - diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/protocol/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/protocol/index.rst.txt deleted file mode 100644 index 03a4fc46..00000000 --- a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/protocol/index.rst.txt +++ /dev/null @@ -1,94 +0,0 @@ -PowerPlatform.Dataverse.models.protocol -======================================= - -.. py:module:: PowerPlatform.Dataverse.models.protocol - -.. autoapi-nested-parse:: - - DataverseModel structural Protocol for typed entity integration. - - - -Classes -------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.models.protocol.DataverseModel - - -Module Contents ---------------- - -.. py:class:: DataverseModel - - Bases: :py:obj:`Protocol` - - - Structural Protocol enabling typed entity instances to be passed to - ``records.create()`` and ``records.update()``. - - Implement this Protocol on any entity class (dataclass, Pydantic model, - hand-rolled) to enable it to be passed directly to CRUD operations without - specifying the table name or converting to dict manually. - - Required class variables: - - - ``__entity_logical_name__`` — Dataverse logical entity name (e.g. ``"account"``) - - ``__entity_set_name__`` — OData entity set name (e.g. ``"accounts"``) - - Required instance methods: - - - ``to_dict()`` — return record payload as ``dict`` - - ``from_dict(data)`` — classmethod to reconstruct from a response ``dict`` - - Example:: - - from dataclasses import dataclass - from PowerPlatform.Dataverse import DataverseModel - - @dataclass - class Account: - __entity_logical_name__ = "account" - __entity_set_name__ = "accounts" - name: str = "" - telephone1: str = "" - - def to_dict(self) -> dict: - return {"name": self.name, "telephone1": self.telephone1} - - @classmethod - def from_dict(cls, data: dict) -> "Account": - return cls( - name=data.get("name", ""), - telephone1=data.get("telephone1", ""), - ) - - # isinstance() works today — Protocol is runtime_checkable: - assert isinstance(Account(), DataverseModel) - - # Type your own helpers against the Protocol now: - def save(entity: DataverseModel) -> None: - data = entity.to_dict() - client.records.create(entity.__entity_logical_name__, data) - - .. note:: - - Direct dispatch (``client.records.create(entity)`` without a table name - or dict) is not yet supported and will be added in a future release. - - - .. py:method:: to_dict() -> dict - - Return the record payload as a plain dictionary. - - - - .. py:method:: from_dict(data: dict) -> DataverseModel - :classmethod: - - - Reconstruct an instance from a response dictionary. - - - diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/query_builder/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/query_builder/index.rst.txt deleted file mode 100644 index 9e8a9583..00000000 --- a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/query_builder/index.rst.txt +++ /dev/null @@ -1,332 +0,0 @@ -PowerPlatform.Dataverse.models.query_builder -============================================ - -.. py:module:: PowerPlatform.Dataverse.models.query_builder - -.. autoapi-nested-parse:: - - Fluent query builder for constructing OData queries. - - Provides a type-safe, discoverable interface for building complex queries - against Dataverse tables with method chaining. - - Example:: - - # Via client (recommended) -- flat iteration over records - from PowerPlatform.Dataverse.models import col - - for record in (client.query.builder("account") - .select("name", "revenue") - .where(col("statecode") == 0) - .where(col("revenue") > 1_000_000) - .order_by("revenue", descending=True) - .top(100) - .execute()): - print(record["name"]) - - # With composable expression tree - from PowerPlatform.Dataverse.models import col, raw - - for record in (client.query.builder("account") - .select("name", "revenue") - .where((col("statecode") == 0) | (col("statecode") == 1)) - .where(col("revenue") > 100000) - .top(100) - .execute()): - print(record["name"]) - - # Lazy paged iteration (one QueryResult per HTTP page) - for page in (client.query.builder("account") - .select("name") - .execute_pages()): - process_batch(page) - - # Get results as a pandas DataFrame - df = (client.query.builder("account") - .select("name", "telephone1") - .where(col("statecode") == 0) - .top(100) - .execute() - .to_dataframe()) - - - -Classes -------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.models.query_builder.QueryParams - PowerPlatform.Dataverse.models.query_builder.ExpandOption - PowerPlatform.Dataverse.models.query_builder.QueryBuilder - - -Module Contents ---------------- - -.. py:class:: QueryParams - - Bases: :py:obj:`TypedDict` - - - Typed dictionary returned by ``QueryBuilder.build()``. - - Provides IDE autocomplete when passing build results to - ``client.records.list()`` manually. - - Initialize self. See help(type(self)) for accurate signature. - - - .. py:attribute:: table - :type: str - - - .. py:attribute:: select - :type: List[str] - - - .. py:attribute:: filter - :type: str - - - .. py:attribute:: orderby - :type: List[str] - - - .. py:attribute:: expand - :type: List[str] - - - .. py:attribute:: top - :type: int - - - .. py:attribute:: page_size - :type: int - - - .. py:attribute:: count - :type: bool - - - .. py:attribute:: include_annotations - :type: str - - -.. py:class:: ExpandOption(relation: str) - - Structured options for an ``$expand`` navigation property. - - Allows specifying nested ``$select``, ``$filter``, ``$orderby``, and - ``$top`` options for a single navigation property expansion, following - the OData ``$expand`` syntax. - - :param relation: Navigation property name (case-sensitive). - :type relation: str - - Example:: - - # Expand Account_Tasks with nested options - opt = (ExpandOption("Account_Tasks") - .select("subject", "createdon") - .filter("contains(subject,'Task')") - .order_by("createdon", descending=True) - .top(5)) - - query = (client.query.builder("account") - .select("name") - .expand(opt) - .execute()) - - - .. py:attribute:: relation - - - .. py:method:: select(*columns: str) -> ExpandOption - - Select specific columns from the expanded entity. - - :param columns: Column names to select. - :return: Self for method chaining. - - - - .. py:method:: filter(filter_str: str) -> ExpandOption - - Filter the expanded collection. - - :param filter_str: OData ``$filter`` expression. - :return: Self for method chaining. - - - - .. py:method:: order_by(column: str, descending: bool = False) -> ExpandOption - - Sort the expanded collection. - - :param column: Column name to sort by. - :param descending: Sort descending if ``True``. - :return: Self for method chaining. - - - - .. py:method:: top(count: int) -> ExpandOption - - Limit expanded results. - - :param count: Maximum number of expanded records. - :return: Self for method chaining. - - - - .. py:method:: to_odata() -> str - - Compile to OData ``$expand`` syntax. - - :return: OData expand string like ``"Nav($select=col1,col2;$filter=...)"`` - :rtype: str - - - -.. py:class:: QueryBuilder(table: str) - - Bases: :py:obj:`_QueryBuilderBase` - - - Fluent interface for building and executing OData queries against a sync client. - - Provides method chaining for constructing complex queries with - composable filter expressions. Can be used standalone (via ``build()``) - or bound to a client (via :meth:`execute`). - - :param table: Table schema name to query. - :type table: str - :raises ValueError: If ``table`` is empty. - - .. rubric:: Example - - Standalone query construction:: - - from PowerPlatform.Dataverse.models import col - - query = (QueryBuilder("account") - .select("name") - .where(col("statecode") == 0) - .top(10)) - params = query.build() - # {"table": "account", "select": ["name"], - # "filter": "statecode eq 0", "top": 10} - - - .. py:method:: execute(*, by_page=_BY_PAGE_UNSET) -> Union[PowerPlatform.Dataverse.models.record.QueryResult, Iterator[PowerPlatform.Dataverse.models.record.QueryResult]] - - Execute the query and return results. - - Returns a :class:`~PowerPlatform.Dataverse.models.record.QueryResult` - with all pages collected. Use :meth:`execute_pages` for lazy per-page - iteration. - - This method is only available when the QueryBuilder was created - via ``client.query.builder(table)``. Standalone ``QueryBuilder`` - instances should use ``build()`` to get parameters and pass them - to ``client.records.list()`` manually. - - At least one of ``select()``, ``where()``, or ``top()`` must be - called before ``execute()``; otherwise a :class:`ValueError` is - raised to prevent accidental full-table scans. - - .. deprecated:: - The ``by_page`` parameter is deprecated. Use :meth:`execute_pages` - for lazy per-page iteration, or plain ``execute()`` (no flag) for - the default eager result. - - :return: :class:`~PowerPlatform.Dataverse.models.record.QueryResult` - with all pages collected (default), or page iterator (deprecated - ``by_page=True``). - :rtype: QueryResult or Iterator[QueryResult] - :raises ValueError: If no ``select``, ``where``, or ``top`` - constraint has been set. - :raises RuntimeError: If the query was not created via - ``client.query.builder()``. - - Example:: - - from PowerPlatform.Dataverse.models import col - - for record in (client.query.builder("account") - .select("name") - .where(col("statecode") == 0) - .execute()): - print(record["name"]) - - - - .. py:method:: execute_pages() -> Iterator[PowerPlatform.Dataverse.models.record.QueryResult] - - Lazily yield one :class:`~PowerPlatform.Dataverse.models.record.QueryResult` - per HTTP page. - - Each iteration triggers a network request via ``@odata.nextLink``. - One-shot — do not iterate more than once. - - At least one of ``select()``, ``where()``, or ``top()`` must be - called before ``execute_pages()``; otherwise a :class:`ValueError` is - raised to prevent accidental full-table scans. - - :return: Iterator of per-page :class:`~PowerPlatform.Dataverse.models.record.QueryResult`. - :rtype: Iterator[QueryResult] - :raises ValueError: If no ``select``, ``where``, or ``top`` - constraint has been set. - :raises RuntimeError: If the query was not created via - ``client.query.builder()``. - - Example:: - - from PowerPlatform.Dataverse.models import col - - for page in (client.query.builder("account") - .select("name") - .where(col("statecode") == 0) - .execute_pages()): - process(page.to_dataframe()) - - - - .. py:method:: to_dataframe() -> pandas.DataFrame - - Execute the query and return results as a pandas DataFrame. - - .. deprecated:: - Use ``QueryBuilder.execute().to_dataframe()`` instead. - ``QueryBuilder.to_dataframe()`` will be removed in a future release. - - All pages are consolidated into a single DataFrame. - - This method is only available when the QueryBuilder was created - via ``client.query.builder(table)``. - - At least one of ``select()``, ``where()``, or ``top()`` must be - called before ``to_dataframe()``; otherwise a :class:`ValueError` - is raised to prevent accidental full-table scans. - - :return: DataFrame containing all matching records. Returns an empty - DataFrame when no records match. - :rtype: ~pandas.DataFrame - :raises ValueError: If no ``select``, ``where``, or ``top`` - constraint has been set. - :raises RuntimeError: If the query was not created via - ``client.query.builder()``. - - Example:: - - from PowerPlatform.Dataverse.models import col - - df = (client.query.builder("account") - .select("name", "telephone1") - .where(col("statecode") == 0) - .top(100) - .execute() - .to_dataframe()) - - - diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/record/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/record/index.rst.txt deleted file mode 100644 index d92274ed..00000000 --- a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/record/index.rst.txt +++ /dev/null @@ -1,152 +0,0 @@ -PowerPlatform.Dataverse.models.record -===================================== - -.. py:module:: PowerPlatform.Dataverse.models.record - -.. autoapi-nested-parse:: - - Record data model for Dataverse entities. - - - -Classes -------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.models.record.Record - PowerPlatform.Dataverse.models.record.QueryResult - - -Module Contents ---------------- - -.. py:class:: Record - - Strongly-typed Dataverse record with dict-like backward compatibility. - - Wraps raw OData response data into a structured object while preserving - ``result["key"]`` access patterns for existing code. - - :param id: Record GUID. Empty string if not extracted (e.g. paginated - results, SQL queries). - :type id: :class:`str` - :param table: Table schema name used in the request. - :type table: :class:`str` - :param data: Record field data as key-value pairs. - :type data: :class:`dict` - :param etag: ETag for optimistic concurrency, extracted from - ``@odata.etag`` in the API response. - :type etag: :class:`str` or None - - Example:: - - record = client.records.get("account", account_id, select=["name"]) - print(record.id) # structured access - print(record["name"]) # dict-like access (backward compat) - - - .. py:attribute:: id - :type: str - :value: '' - - - - .. py:attribute:: table - :type: str - :value: '' - - - - .. py:attribute:: data - :type: Dict[str, Any] - - - .. py:attribute:: etag - :type: Optional[str] - :value: None - - - - .. py:method:: get(key: str, default: Any = None) -> Any - - Return value for *key*, or *default* if not present. - - - - .. py:method:: keys() -> KeysView[str] - - Return data keys. - - - - .. py:method:: values() -> ValuesView[Any] - - Return data values. - - - - .. py:method:: items() -> ItemsView[str, Any] - - Return data items. - - - - .. py:method:: from_api_response(table: str, response_data: Dict[str, Any], *, record_id: str = '') -> Record - :classmethod: - - - Create a :class:`Record` from a raw OData API response. - - Strips ``@odata.*`` annotation keys from the data and extracts the - ``@odata.etag`` value if present. - - :param table: Table schema name. - :type table: :class:`str` - :param response_data: Raw JSON dict from the OData response. - :type response_data: :class:`dict` - :param record_id: Known record GUID. Pass explicitly when available - (e.g. single-record get). Defaults to empty string. - :type record_id: :class:`str` - :rtype: :class:`Record` - - - - .. py:method:: to_dict() -> Dict[str, Any] - - Return a plain dict copy of the record data (excludes metadata). - - - -.. py:class:: QueryResult(records: List[Record]) - - Iterable wrapper around a list of :class:`Record` objects. - - Returned by :meth:`~PowerPlatform.Dataverse.models.query_builder.QueryBuilder.execute` - (flat mode) and :meth:`~PowerPlatform.Dataverse.operations.records.RecordOperations.list`. - - Backward-compatible: ``for r in result`` continues to work without change. - - :param records: Collected records from all pages. - :type records: list[:class:`Record`] - - - .. py:attribute:: records - :type: List[Record] - - - .. py:method:: first() -> Optional[Record] - - Return the first record, or ``None`` if the result is empty. - - - - .. py:method:: to_dataframe() -> Any - - Return all records as a pandas DataFrame. - - :raises ImportError: If pandas is not installed. - :rtype: ~pandas.DataFrame - - - diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/relationship/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/relationship/index.rst.txt deleted file mode 100644 index 06160b7a..00000000 --- a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/relationship/index.rst.txt +++ /dev/null @@ -1,471 +0,0 @@ -PowerPlatform.Dataverse.models.relationship -=========================================== - -.. py:module:: PowerPlatform.Dataverse.models.relationship - -.. autoapi-nested-parse:: - - Relationship models for Dataverse (input and output). - - - -Classes -------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.models.relationship.CascadeConfiguration - PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata - PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata - PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata - PowerPlatform.Dataverse.models.relationship.RelationshipInfo - - -Module Contents ---------------- - -.. py:class:: CascadeConfiguration - - Defines cascade behavior for relationship operations. - - :param assign: Cascade behavior for assign operations. - :type assign: str - :param delete: Cascade behavior for delete operations. - :type delete: str - :param merge: Cascade behavior for merge operations. - :type merge: str - :param reparent: Cascade behavior for reparent operations. - :type reparent: str - :param share: Cascade behavior for share operations. - :type share: str - :param unshare: Cascade behavior for unshare operations. - :type unshare: str - :param additional_properties: Optional dict of additional properties to include - in the Web API payload (e.g., "Archive", "RollupView"). These are merged - last and can override default values. - :type additional_properties: Optional[Dict[str, Any]] - - Valid values for each parameter: - - "Cascade": Perform the operation on all related records - - "NoCascade": Do not perform the operation on related records - - "RemoveLink": Remove the relationship link but keep the records - - "Restrict": Prevent the operation if related records exist - - - .. py:attribute:: assign - :type: str - :value: 'NoCascade' - - - - .. py:attribute:: delete - :type: str - :value: 'RemoveLink' - - - - .. py:attribute:: merge - :type: str - :value: 'NoCascade' - - - - .. py:attribute:: reparent - :type: str - :value: 'NoCascade' - - - - .. py:attribute:: share - :type: str - :value: 'NoCascade' - - - - .. py:attribute:: unshare - :type: str - :value: 'NoCascade' - - - - .. py:attribute:: additional_properties - :type: Optional[Dict[str, Any]] - :value: None - - - - .. py:method:: to_dict() -> Dict[str, Any] - - Convert to Web API JSON format. - - Example:: - - >>> config = CascadeConfiguration(delete="Cascade", assign="NoCascade") - >>> config.to_dict() - { - 'Assign': 'NoCascade', - 'Delete': 'Cascade', - 'Merge': 'NoCascade', - 'Reparent': 'NoCascade', - 'Share': 'NoCascade', - 'Unshare': 'NoCascade' - } - - - -.. py:class:: LookupAttributeMetadata - - Metadata for a lookup attribute. - - :param schema_name: Schema name for the attribute (e.g., "new_AccountId"). - :type schema_name: str - :param display_name: Display name for the attribute. - :type display_name: Label - :param description: Optional description of the attribute. - :type description: Optional[Label] - :param required_level: Requirement level for the attribute. - :type required_level: str - :param additional_properties: Optional dict of additional properties to include - in the Web API payload. Useful for setting properties like "Targets" (to - specify which entity types the lookup can reference), "LogicalName", - "IsSecured", "IsValidForAdvancedFind", etc. These are merged last and - can override default values. - :type additional_properties: Optional[Dict[str, Any]] - - Valid required_level values: - - "None": The attribute is optional - - "Recommended": The attribute is recommended - - "ApplicationRequired": The attribute is required - - - .. py:attribute:: schema_name - :type: str - - - .. py:attribute:: display_name - :type: PowerPlatform.Dataverse.models.labels.Label - - - .. py:attribute:: description - :type: Optional[PowerPlatform.Dataverse.models.labels.Label] - :value: None - - - - .. py:attribute:: required_level - :type: str - :value: 'None' - - - - .. py:attribute:: additional_properties - :type: Optional[Dict[str, Any]] - :value: None - - - - .. py:method:: to_dict() -> Dict[str, Any] - - Convert to Web API JSON format. - - Example:: - - >>> lookup = LookupAttributeMetadata( - ... schema_name="new_AccountId", - ... display_name=Label([LocalizedLabel("Account", 1033)]) - ... ) - >>> lookup.to_dict() - { - '@odata.type': 'Microsoft.Dynamics.CRM.LookupAttributeMetadata', - 'SchemaName': 'new_AccountId', - 'AttributeType': 'Lookup', - 'AttributeTypeName': {'Value': 'LookupType'}, - 'DisplayName': {...}, - 'RequiredLevel': {'Value': 'None', 'CanBeChanged': True, ...} - } - - - -.. py:class:: OneToManyRelationshipMetadata - - Metadata for a one-to-many entity relationship. - - :param schema_name: Schema name for the relationship (e.g., "new_Account_Orders"). - :type schema_name: str - :param referenced_entity: Logical name of the referenced (parent) entity. - :type referenced_entity: str - :param referencing_entity: Logical name of the referencing (child) entity. - :type referencing_entity: str - :param referenced_attribute: Attribute on the referenced entity (typically the primary key). - :type referenced_attribute: str - :param cascade_configuration: Cascade behavior configuration. - :type cascade_configuration: CascadeConfiguration - :param referencing_attribute: Optional name for the referencing attribute (usually auto-generated). - :type referencing_attribute: Optional[str] - :param additional_properties: Optional dict of additional properties to include - in the Web API payload. Useful for setting inherited properties like - "IsValidForAdvancedFind", "IsCustomizable", "SecurityTypes", etc. - These are merged last and can override default values. - :type additional_properties: Optional[Dict[str, Any]] - - - .. py:attribute:: schema_name - :type: str - - - .. py:attribute:: referenced_entity - :type: str - - - .. py:attribute:: referencing_entity - :type: str - - - .. py:attribute:: referenced_attribute - :type: str - - - .. py:attribute:: cascade_configuration - :type: CascadeConfiguration - - - .. py:attribute:: referencing_attribute - :type: Optional[str] - :value: None - - - - .. py:attribute:: additional_properties - :type: Optional[Dict[str, Any]] - :value: None - - - - .. py:method:: to_dict() -> Dict[str, Any] - - Convert to Web API JSON format. - - Example:: - - >>> rel = OneToManyRelationshipMetadata( - ... schema_name="new_account_orders", - ... referenced_entity="account", - ... referencing_entity="new_order", - ... referenced_attribute="accountid" - ... ) - >>> rel.to_dict() - { - '@odata.type': 'Microsoft.Dynamics.CRM.OneToManyRelationshipMetadata', - 'SchemaName': 'new_account_orders', - 'ReferencedEntity': 'account', - 'ReferencingEntity': 'new_order', - 'ReferencedAttribute': 'accountid', - 'CascadeConfiguration': {...} - } - - - -.. py:class:: ManyToManyRelationshipMetadata - - Metadata for a many-to-many entity relationship. - - :param schema_name: Schema name for the relationship. - :type schema_name: str - :param entity1_logical_name: Logical name of the first entity. - :type entity1_logical_name: str - :param entity2_logical_name: Logical name of the second entity. - :type entity2_logical_name: str - :param intersect_entity_name: Name for the intersect table (defaults to schema_name if not provided). - :type intersect_entity_name: Optional[str] - :param additional_properties: Optional dict of additional properties to include - in the Web API payload. Useful for setting inherited properties like - "IsValidForAdvancedFind", "IsCustomizable", "SecurityTypes", or direct - properties like "Entity1NavigationPropertyName". These are merged last - and can override default values. - :type additional_properties: Optional[Dict[str, Any]] - - - .. py:attribute:: schema_name - :type: str - - - .. py:attribute:: entity1_logical_name - :type: str - - - .. py:attribute:: entity2_logical_name - :type: str - - - .. py:attribute:: intersect_entity_name - :type: Optional[str] - :value: None - - - - .. py:attribute:: additional_properties - :type: Optional[Dict[str, Any]] - :value: None - - - - .. py:method:: to_dict() -> Dict[str, Any] - - Convert to Web API JSON format. - - Example:: - - >>> rel = ManyToManyRelationshipMetadata( - ... schema_name="new_account_contact", - ... entity1_logical_name="account", - ... entity2_logical_name="contact" - ... ) - >>> rel.to_dict() - { - '@odata.type': 'Microsoft.Dynamics.CRM.ManyToManyRelationshipMetadata', - 'SchemaName': 'new_account_contact', - 'Entity1LogicalName': 'account', - 'Entity2LogicalName': 'contact', - 'IntersectEntityName': 'new_account_contact' - } - - - -.. py:class:: RelationshipInfo - - Typed return model for relationship metadata. - - Returned by :meth:`~PowerPlatform.Dataverse.operations.tables.TableOperations.create_one_to_many_relationship`, - :meth:`~PowerPlatform.Dataverse.operations.tables.TableOperations.create_many_to_many_relationship`, - :meth:`~PowerPlatform.Dataverse.operations.tables.TableOperations.get_relationship`, and - :meth:`~PowerPlatform.Dataverse.operations.tables.TableOperations.create_lookup_field`. - - :param relationship_id: Relationship metadata GUID. - :type relationship_id: :class:`str` or None - :param relationship_schema_name: Relationship schema name. - :type relationship_schema_name: :class:`str` - :param relationship_type: Either ``"one_to_many"`` or ``"many_to_many"``. - :type relationship_type: :class:`str` - :param lookup_schema_name: Lookup field schema name (one-to-many only). - :type lookup_schema_name: :class:`str` or None - :param referenced_entity: Parent entity logical name (one-to-many only). - :type referenced_entity: :class:`str` or None - :param referencing_entity: Child entity logical name (one-to-many only). - :type referencing_entity: :class:`str` or None - :param entity1_logical_name: First entity logical name (many-to-many only). - :type entity1_logical_name: :class:`str` or None - :param entity2_logical_name: Second entity logical name (many-to-many only). - :type entity2_logical_name: :class:`str` or None - - Example:: - - result = client.tables.create_one_to_many_relationship(lookup, relationship) - print(result.relationship_schema_name) - print(result.lookup_schema_name) - - - .. py:attribute:: relationship_id - :type: Optional[str] - :value: None - - - - .. py:attribute:: relationship_schema_name - :type: str - :value: '' - - - - .. py:attribute:: relationship_type - :type: str - :value: '' - - - - .. py:attribute:: lookup_schema_name - :type: Optional[str] - :value: None - - - - .. py:attribute:: referenced_entity - :type: Optional[str] - :value: None - - - - .. py:attribute:: referencing_entity - :type: Optional[str] - :value: None - - - - .. py:attribute:: entity1_logical_name - :type: Optional[str] - :value: None - - - - .. py:attribute:: entity2_logical_name - :type: Optional[str] - :value: None - - - - .. py:method:: from_one_to_many(*, relationship_id: Optional[str], relationship_schema_name: str, lookup_schema_name: str, referenced_entity: str, referencing_entity: str) -> RelationshipInfo - :classmethod: - - - Create from a one-to-many relationship result. - - :param relationship_id: Relationship metadata GUID. - :type relationship_id: :class:`str` or None - :param relationship_schema_name: Relationship schema name. - :type relationship_schema_name: :class:`str` - :param lookup_schema_name: Lookup field schema name. - :type lookup_schema_name: :class:`str` - :param referenced_entity: Parent entity logical name. - :type referenced_entity: :class:`str` - :param referencing_entity: Child entity logical name. - :type referencing_entity: :class:`str` - :rtype: :class:`RelationshipInfo` - - - - .. py:method:: from_many_to_many(*, relationship_id: Optional[str], relationship_schema_name: str, entity1_logical_name: str, entity2_logical_name: str) -> RelationshipInfo - :classmethod: - - - Create from a many-to-many relationship result. - - :param relationship_id: Relationship metadata GUID. - :type relationship_id: :class:`str` or None - :param relationship_schema_name: Relationship schema name. - :type relationship_schema_name: :class:`str` - :param entity1_logical_name: First entity logical name. - :type entity1_logical_name: :class:`str` - :param entity2_logical_name: Second entity logical name. - :type entity2_logical_name: :class:`str` - :rtype: :class:`RelationshipInfo` - - - - .. py:method:: from_api_response(response_data: Dict[str, Any]) -> RelationshipInfo - :classmethod: - - - Create from a raw Dataverse Web API response. - - Detects one-to-many vs many-to-many from the ``@odata.type`` field - in the response and maps PascalCase keys to snake_case attributes. - Dataverse only supports these two relationship types; an unrecognized - ``@odata.type`` raises :class:`ValueError`. - - :param response_data: Raw relationship metadata from the Web API. - :type response_data: :class:`dict` - :rtype: :class:`RelationshipInfo` - :raises ValueError: If the ``@odata.type`` is not a recognized - relationship type. - - - diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/table_info/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/table_info/index.rst.txt deleted file mode 100644 index fd73d890..00000000 --- a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/table_info/index.rst.txt +++ /dev/null @@ -1,305 +0,0 @@ -PowerPlatform.Dataverse.models.table_info -========================================= - -.. py:module:: PowerPlatform.Dataverse.models.table_info - -.. autoapi-nested-parse:: - - Table and column metadata models for Dataverse. - - - -Classes -------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.models.table_info.ColumnInfo - PowerPlatform.Dataverse.models.table_info.TableInfo - PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo - - -Module Contents ---------------- - -.. py:class:: ColumnInfo - - Column metadata from a Dataverse table definition. - - :param schema_name: Column schema name (e.g. ``"new_Price"``). - :type schema_name: :class:`str` - :param logical_name: Column logical name (lowercase). - :type logical_name: :class:`str` - :param type: Column type string (e.g. ``"String"``, ``"Integer"``). - :type type: :class:`str` - :param is_primary: Whether this is the primary name column. - :type is_primary: :class:`bool` - :param is_required: Whether the column is required. - :type is_required: :class:`bool` - :param max_length: Maximum length for string columns. - :type max_length: :class:`int` or None - :param display_name: Human-readable display name. - :type display_name: :class:`str` or None - :param description: Column description. - :type description: :class:`str` or None - - - .. py:attribute:: schema_name - :type: str - :value: '' - - - - .. py:attribute:: logical_name - :type: str - :value: '' - - - - .. py:attribute:: type - :type: str - :value: '' - - - - .. py:attribute:: is_primary - :type: bool - :value: False - - - - .. py:attribute:: is_required - :type: bool - :value: False - - - - .. py:attribute:: max_length - :type: Optional[int] - :value: None - - - - .. py:attribute:: display_name - :type: Optional[str] - :value: None - - - - .. py:attribute:: description - :type: Optional[str] - :value: None - - - - .. py:method:: from_api_response(response_data: Dict[str, Any]) -> ColumnInfo - :classmethod: - - - Create from a raw Dataverse ``AttributeMetadata`` API response. - - :param response_data: Raw attribute metadata dict (PascalCase keys). - :type response_data: :class:`dict` - :rtype: :class:`ColumnInfo` - - - -.. py:class:: TableInfo - - Table metadata with dict-like backward compatibility. - - Supports both new attribute access (``info.schema_name``) and legacy - dict-key access (``info["table_schema_name"]``) for backward - compatibility with code written against the raw dict API. - - :param schema_name: Table schema name (e.g. ``"Account"``). - :type schema_name: :class:`str` - :param logical_name: Table logical name (lowercase). - :type logical_name: :class:`str` - :param entity_set_name: OData entity set name. - :type entity_set_name: :class:`str` - :param metadata_id: Metadata GUID. - :type metadata_id: :class:`str` - :param display_name: Human-readable display name. - :type display_name: :class:`str` or None - :param description: Table description. - :type description: :class:`str` or None - :param columns: Column metadata (when retrieved). - :type columns: list[ColumnInfo] or None - :param columns_created: Column schema names created with the table. - :type columns_created: list[str] or None - - Example:: - - info = client.tables.create("new_Product", {"new_Price": "decimal"}) - print(info.schema_name) # new attribute access - print(info["table_schema_name"]) # legacy dict-key access - - - .. py:attribute:: schema_name - :type: str - :value: '' - - - - .. py:attribute:: logical_name - :type: str - :value: '' - - - - .. py:attribute:: entity_set_name - :type: str - :value: '' - - - - .. py:attribute:: metadata_id - :type: str - :value: '' - - - - .. py:attribute:: primary_name_attribute - :type: Optional[str] - :value: None - - - - .. py:attribute:: primary_id_attribute - :type: Optional[str] - :value: None - - - - .. py:attribute:: display_name - :type: Optional[str] - :value: None - - - - .. py:attribute:: description - :type: Optional[str] - :value: None - - - - .. py:attribute:: columns - :type: Optional[List[ColumnInfo]] - :value: None - - - - .. py:attribute:: columns_created - :type: Optional[List[str]] - :value: None - - - - .. py:method:: get(key: str, default: Any = None) -> Any - - Return value for *key*, or *default* if not present. - - - - .. py:method:: keys() -> KeysView[str] - - Return legacy dict keys. - - - - .. py:method:: values() -> List[Any] - - Return values corresponding to legacy dict keys. - - - - .. py:method:: items() -> List[tuple] - - Return (legacy_key, value) pairs. - - - - .. py:method:: from_dict(data: Dict[str, Any]) -> TableInfo - :classmethod: - - - Create from an SDK internal dict (snake_case keys). - - This handles the dict format returned by ``_create_table`` and - ``_get_table_info`` in the OData layer. - - :param data: Dictionary with SDK snake_case keys. - :type data: :class:`dict` - :rtype: :class:`TableInfo` - - - - .. py:method:: from_api_response(response_data: Dict[str, Any]) -> TableInfo - :classmethod: - - - Create from a raw Dataverse ``EntityDefinition`` API response. - - :param response_data: Raw entity metadata dict (PascalCase keys). - :type response_data: :class:`dict` - :rtype: :class:`TableInfo` - - - - .. py:method:: to_dict() -> Dict[str, Any] - - Return a dict with legacy keys for backward compatibility. - - - -.. py:class:: AlternateKeyInfo - - Alternate key metadata for a Dataverse table. - - :param metadata_id: Key metadata GUID. - :type metadata_id: :class:`str` - :param schema_name: Key schema name. - :type schema_name: :class:`str` - :param key_attributes: List of column logical names that compose the key. - :type key_attributes: list[str] - :param status: Index creation status (``"Active"``, ``"Pending"``, ``"InProgress"``, ``"Failed"``). - :type status: :class:`str` - - - .. py:attribute:: metadata_id - :type: str - :value: '' - - - - .. py:attribute:: schema_name - :type: str - :value: '' - - - - .. py:attribute:: key_attributes - :type: List[str] - :value: [] - - - - .. py:attribute:: status - :type: str - :value: '' - - - - .. py:method:: from_api_response(response_data: Dict[str, Any]) -> AlternateKeyInfo - :classmethod: - - - Create from raw EntityKeyMetadata API response. - - :param response_data: Raw key metadata dictionary from the Web API. - :type response_data: :class:`dict` - :rtype: :class:`AlternateKeyInfo` - - - diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/upsert/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/upsert/index.rst.txt deleted file mode 100644 index b6568676..00000000 --- a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/models/upsert/index.rst.txt +++ /dev/null @@ -1,54 +0,0 @@ -PowerPlatform.Dataverse.models.upsert -===================================== - -.. py:module:: PowerPlatform.Dataverse.models.upsert - -.. autoapi-nested-parse:: - - Upsert data models for the Dataverse SDK. - - - -Classes -------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.models.upsert.UpsertItem - - -Module Contents ---------------- - -.. py:class:: UpsertItem - - Represents a single upsert operation targeting a record by its alternate key. - - Used with :meth:`~PowerPlatform.Dataverse.operations.records.RecordOperations.upsert` - to upsert one or more records identified by alternate keys rather than primary GUIDs. - - :param alternate_key: Dictionary mapping alternate key attribute names to their values. - String values are automatically quoted and escaped in the OData URL. Integer and - other non-string values are included without quotes. - :type alternate_key: dict[str, Any] - :param record: Dictionary of attribute names to values for the record payload. - Keys are automatically lowercased. Picklist labels are resolved to integer option - values when a matching option set is found. - :type record: dict[str, Any] - - Example:: - - item = UpsertItem( - alternate_key={"accountnumber": "ACC-001", "address1_postalcode": "98052"}, - record={"name": "Contoso Ltd", "telephone1": "555-0100"}, - ) - - - .. py:attribute:: alternate_key - :type: Dict[str, Any] - - - .. py:attribute:: record - :type: Dict[str, Any] - - diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/batch/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/batch/index.rst.txt deleted file mode 100644 index 160530b7..00000000 --- a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/batch/index.rst.txt +++ /dev/null @@ -1,741 +0,0 @@ -PowerPlatform.Dataverse.operations.batch -======================================== - -.. py:module:: PowerPlatform.Dataverse.operations.batch - -.. autoapi-nested-parse:: - - Batch operation namespaces for the Dataverse SDK. - - - -Classes -------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.operations.batch.ChangeSetRecordOperations - PowerPlatform.Dataverse.operations.batch.ChangeSet - PowerPlatform.Dataverse.operations.batch.BatchRecordOperations - PowerPlatform.Dataverse.operations.batch.BatchTableOperations - PowerPlatform.Dataverse.operations.batch.BatchQueryOperations - PowerPlatform.Dataverse.operations.batch.BatchDataFrameOperations - PowerPlatform.Dataverse.operations.batch.BatchRequest - PowerPlatform.Dataverse.operations.batch.BatchOperations - - -Module Contents ---------------- - -.. py:class:: ChangeSetRecordOperations(cs_internal: PowerPlatform.Dataverse.data._batch._ChangeSet) - - Record write operations available inside a :class:`ChangeSet`. - - Mirrors ``client.records`` but restricted to single-record forms (no bulk - create/update/delete). Only write operations are allowed — GET is not - permitted inside a changeset. - - Do not instantiate directly; use ``ChangeSet.records``. - - - .. py:method:: create(table: str, data: Dict[str, Any]) -> str - - Add a single-record create to this changeset. - - :param table: Table schema name (e.g. ``"account"``). - :type table: :class:`str` - :param data: Column values for the new record. - :type data: dict[str, typing.Any] - :returns: A content-ID reference string (e.g. ``"$1"``) usable in - subsequent operations within this changeset as a URI reference - in ``@odata.bind`` fields or as ``record_id`` in - :meth:`update` / :meth:`delete`. - :rtype: :class:`str` - - Example:: - - with batch.changeset() as cs: - lead_ref = cs.records.create("lead", {"firstname": "Ada"}) - cs.records.create("account", { - "name": "Babbage", - "originatingleadid@odata.bind": lead_ref, - }) - - - - .. py:method:: update(table: str, record_id: str, changes: Dict[str, Any]) -> None - - Add a single-record update to this changeset. - - :param table: Table schema name. Ignored when ``record_id`` is a - content-ID reference. - :type table: :class:`str` - :param record_id: GUID or a content-ID reference (e.g. ``"$1"``) - returned by a prior :meth:`create` in this changeset. - :type record_id: :class:`str` - :param changes: Column values to update. - :type changes: dict[str, typing.Any] - - - - .. py:method:: delete(table: str, record_id: str) -> None - - Add a single-record delete to this changeset. - - :param table: Table schema name. Ignored when ``record_id`` is a - content-ID reference. - :type table: :class:`str` - :param record_id: GUID or a content-ID reference (e.g. ``"$1"``). - :type record_id: :class:`str` - - - -.. py:class:: ChangeSet(internal: PowerPlatform.Dataverse.data._batch._ChangeSet) - - A transactional group of single-record write operations. - - All operations succeed or are rolled back together. Use as a context - manager or call ``records`` to add operations directly. - - Do not instantiate directly; use :meth:`BatchRequest.changeset`. - - Example:: - - with batch.changeset() as cs: - ref = cs.records.create("contact", {"firstname": "Alice"}) - cs.records.update("account", account_id, { - "primarycontactid@odata.bind": ref - }) - - - .. py:attribute:: records - - -.. py:class:: BatchRecordOperations(batch: _BatchContext) - - Record operations on a :class:`BatchRequest`. - - Mirrors ``client.records``: same method names, same signatures. - All methods return ``None``; results are available via - :class:`~PowerPlatform.Dataverse.models.batch.BatchResult` after - :meth:`BatchRequest.execute`. - - GA methods: :meth:`retrieve` (single record) and :meth:`list` (multi-record, - single page). :meth:`get` is deprecated — use :meth:`retrieve` instead. - - Do not instantiate directly; use ``batch.records``. - - - .. py:method:: create(table: str, data: Union[Dict[str, Any], List[Dict[str, Any]]]) -> None - - Add a create operation to the batch. - - A single dict creates one record (POST entity_set). - A list of dicts creates all records via the ``CreateMultiple`` action - (one batch item). - - :param table: Table schema name (e.g. ``"account"``). - :type table: :class:`str` - :param data: Single record dict or list of record dicts. - :type data: dict or list[dict] - - - - .. py:method:: update(table: str, ids: Union[str, List[str]], changes: Union[Dict[str, Any], List[Dict[str, Any]]]) -> None - - Add an update operation to the batch. - - - **Single** ``(table, "guid", {...})`` -> one PATCH request. - - **Broadcast** ``(table, [id1, id2], {...})`` -> one ``UpdateMultiple`` POST. - - **Paired** ``(table, [id1, id2], [{...}, {...}])`` -> one ``UpdateMultiple`` POST. - - :param table: Table schema name. - :type table: :class:`str` - :param ids: Single GUID or list of GUIDs. - :type ids: str or list[str] - :param changes: Single dict (single/broadcast) or list of dicts (paired). - :type changes: dict or list[dict] - - - - .. py:method:: delete(table: str, ids: Union[str, List[str]], *, use_bulk_delete: bool = True) -> None - - Add a delete operation to the batch. - - - **Single** ``(table, "guid")`` -> one DELETE request. - - **List + use_bulk_delete=True** (default) -> one ``BulkDelete`` POST. - The async job ID will be available in ``BatchItemResponse.data["JobId"]``. - - **List + use_bulk_delete=False** -> one DELETE per record. - - :param table: Table schema name. - :type table: :class:`str` - :param ids: Single GUID or list of GUIDs. - :type ids: str or list[str] - :param use_bulk_delete: When True (default) and ``ids`` is a list, use the - BulkDelete action. When False, delete records individually. - :type use_bulk_delete: :class:`bool` - - - - .. py:method:: get(table: str, record_id: str, *, select: Optional[List[str]] = None) -> None - - Add a single-record get operation to the batch. - - .. deprecated:: - Use :meth:`retrieve` instead. ``batch.records.get()`` is deprecated - and will be removed in a future release. - - :param table: Table schema name. - :type table: :class:`str` - :param record_id: GUID of the record to retrieve. - :type record_id: :class:`str` - :param select: Optional list of column names to include. - :type select: list[str] or None - - - - .. py:method:: upsert(table: str, items: List[Union[PowerPlatform.Dataverse.models.upsert.UpsertItem, Dict[str, Any]]]) -> None - - Add an upsert operation to the batch. - - Mirrors :meth:`~PowerPlatform.Dataverse.operations.records.RecordOperations.upsert`: - a single item becomes a PATCH request using the alternate key; multiple items - become one ``UpsertMultiple`` POST. - - Each item must be a :class:`~PowerPlatform.Dataverse.models.upsert.UpsertItem` - or a plain ``dict`` with ``"alternate_key"`` and ``"record"`` keys (both dicts). - - :param table: Table schema name (e.g. ``"account"``). - :type table: :class:`str` - :param items: Non-empty list of :class:`~PowerPlatform.Dataverse.models.upsert.UpsertItem` - instances or equivalent dicts. - :type items: list[~PowerPlatform.Dataverse.models.upsert.UpsertItem] - - :raises TypeError: If ``items`` is not a non-empty list, or if any element is - neither a :class:`~PowerPlatform.Dataverse.models.upsert.UpsertItem` nor a - dict with ``"alternate_key"`` and ``"record"`` keys. - - Example:: - - from PowerPlatform.Dataverse.models import UpsertItem - - batch.records.upsert("account", [ - UpsertItem( - alternate_key={"accountnumber": "ACC-001"}, - record={"name": "Contoso Ltd"}, - ), - UpsertItem( - alternate_key={"accountnumber": "ACC-002"}, - record={"name": "Fabrikam Inc"}, - ), - ]) - - - - .. py:method:: retrieve(table: str, record_id: str, *, select: Optional[List[str]] = None, expand: Optional[List[str]] = None, include_annotations: Optional[str] = None) -> None - - Add a single-record retrieve operation to the batch. - - GA replacement for the deprecated :meth:`get`. Enqueues a GET request - for one record by its GUID. The response body will be available in - :attr:`~PowerPlatform.Dataverse.models.batch.BatchItemResponse.data` - after :meth:`BatchRequest.execute`. - - :param table: Table schema name (e.g. ``"account"``). - :type table: :class:`str` - :param record_id: GUID of the record to retrieve. - :type record_id: :class:`str` - :param select: Optional list of column logical names to include. - :type select: list[str] or None - :param expand: Optional list of navigation properties to expand. - Navigation property names are case-sensitive and must match the - entity's ``$metadata``. - :type expand: list[str] or None - :param include_annotations: OData annotation pattern for the - ``Prefer: odata.include-annotations`` header (e.g. ``"*"`` or - ``"OData.Community.Display.V1.FormattedValue"``), or ``None``. - :type include_annotations: :class:`str` or None - - Example:: - - batch = client.batch.new() - batch.records.retrieve( - "account", account_id, - select=["name", "statuscode"], - expand=["primarycontactid"], - include_annotations="OData.Community.Display.V1.FormattedValue", - ) - result = batch.execute() - record = result.responses[0].data - contact = (record.get("primarycontactid") or {}) - print(contact.get("fullname")) - - - - .. py:method:: list(table: str, *, filter: Optional[Union[str, FilterExpression]] = None, select: Optional[List[str]] = None, orderby: Optional[List[str]] = None, top: Optional[int] = None, expand: Optional[List[str]] = None, page_size: Optional[int] = None, count: bool = False, include_annotations: Optional[str] = None) -> None - - Add a multi-record list operation to the batch (single page, no pagination). - - Enqueues a GET request for multiple records. Because batch requests are - a single HTTP round-trip, pagination (``@odata.nextLink``) is **not** - supported — use ``top`` to bound the result size, or rely on the - server's default page limit. - - The response body (``{"value": [...]}`` JSON) will be available in - :attr:`~PowerPlatform.Dataverse.models.batch.BatchItemResponse.data` - after :meth:`BatchRequest.execute`. - - :param table: Table schema name (e.g. ``"account"``). - :type table: :class:`str` - :param filter: Optional OData ``$filter`` expression or :class:`~PowerPlatform.Dataverse.models.filters.FilterExpression`. - :type filter: str or FilterExpression or None - :param select: Optional list of column logical names to include. - :type select: list[str] or None - :param orderby: Optional list of sort expressions (e.g. ``["name asc"]``). - :type orderby: list[str] or None - :param top: Maximum number of records to return. - :type top: int or None - :param expand: Optional list of navigation properties to expand. - :type expand: list[str] or None - :param page_size: Per-page size hint via ``Prefer: odata.maxpagesize``. - :type page_size: int or None - :param count: If ``True``, adds ``$count=true`` to the request. - :type count: bool - :param include_annotations: OData annotation pattern for the - ``Prefer: odata.include-annotations`` header, or ``None``. - :type include_annotations: :class:`str` or None - - Example:: - - batch = client.batch.new() - batch.records.list( - "account", - filter="statecode eq 0", - select=["name", "statuscode"], - orderby=["name asc"], - top=50, - include_annotations="OData.Community.Display.V1.FormattedValue", - ) - result = batch.execute() - records = result.responses[0].data.get("value", []) - - - -.. py:class:: BatchTableOperations(batch: _BatchContext) - - Table metadata operations on a :class:`BatchRequest`. - - Mirrors ``client.tables`` exactly: same method names, same signatures. - All methods return ``None``; results arrive via - :class:`~PowerPlatform.Dataverse.models.batch.BatchResult`. - - .. note:: - ``tables.delete``, ``tables.add_columns``, and ``tables.remove_columns`` - require a metadata lookup (GET ``EntityDefinitions``) at - :meth:`BatchRequest.execute` time to resolve the table's MetadataId. - This lookup is transparent to the caller. - - .. note:: - ``tables.add_columns`` and ``tables.remove_columns`` each produce one - batch item per column, so they contribute multiple entries to - :attr:`~PowerPlatform.Dataverse.models.batch.BatchResult.responses`. - - Do not instantiate directly; use ``batch.tables``. - - - .. py:method:: create(table: str, columns: Dict[str, Any], *, solution: Optional[str] = None, primary_column: Optional[str] = None, display_name: Optional[str] = None) -> None - - Add a table-create operation to the batch. - - .. note:: - The pre-existence check performed by ``client.tables.create`` is skipped - in batch mode. If the table already exists the server returns an error - in the corresponding :class:`~PowerPlatform.Dataverse.models.batch.BatchItemResponse`. - - :param table: Schema name of the new table (e.g. ``"new_Product"``). - :type table: :class:`str` - :param columns: Mapping of column schema names to type strings or Enum subclasses. - :type columns: dict[str, typing.Any] - :param solution: Optional solution unique name. - :type solution: str or None - :param primary_column: Optional primary column schema name. - :type primary_column: str or None - :param display_name: Human-readable display name for the table. - When omitted, defaults to the table schema name. - :type display_name: str or None - - - - .. py:method:: delete(table: str) -> None - - Add a table-delete operation to the batch. - - The table's ``MetadataId`` is resolved via a GET request at execute time. - - :param table: Schema name of the table to delete. - :type table: :class:`str` - - - - .. py:method:: get(table: str) -> None - - Add a table-metadata-get operation to the batch. - - The response will be in ``BatchItemResponse.data`` after execute. - - :param table: Schema name of the table. - :type table: :class:`str` - - - - .. py:method:: list(*, filter: Optional[str] = None, select: Optional[List[str]] = None) -> None - - Add a list-all-tables operation to the batch. - - Mirrors ``client.tables.list()``. Supply an optional OData - ``$filter`` expression to further narrow the results (combined with - ``IsPrivate eq false`` using ``and``). ``select`` projects - specific property names via ``$select``. - - The response will be in ``BatchItemResponse.data`` after execute. - - :param filter: Additional OData ``$filter`` expression. - :type filter: str or None - :param select: List of property names for ``$select``. - :type select: list[str] or None - - - - .. py:method:: add_columns(table: str, columns: Dict[str, Any]) -> None - - Add column-create operations to the batch (one per column). - - The table's ``MetadataId`` is resolved at execute time. Each column - produces one entry in :attr:`~PowerPlatform.Dataverse.models.batch.BatchResult.responses`. - - :param table: Schema name of the target table. - :type table: :class:`str` - :param columns: Mapping of column schema names to type strings or Enum subclasses. - :type columns: dict[str, typing.Any] - - - - .. py:method:: remove_columns(table: str, columns: Union[str, List[str]]) -> None - - Add column-delete operations to the batch (one per column). - - The table's ``MetadataId`` and each column's ``MetadataId`` are resolved - at execute time. Each column produces one entry in - :attr:`~PowerPlatform.Dataverse.models.batch.BatchResult.responses`. - - :param table: Schema name of the target table. - :type table: :class:`str` - :param columns: Column schema name or list of column schema names to remove. - :type columns: str or list[str] - - - - .. py:method:: create_one_to_many_relationship(lookup: PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata, relationship: PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata, *, solution: Optional[str] = None) -> None - - Add a one-to-many relationship creation to the batch. - - :param lookup: Lookup attribute metadata. - :type lookup: ~PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata - :param relationship: Relationship metadata. - :type relationship: ~PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata - :param solution: Optional solution unique name. - :type solution: str or None - - - - .. py:method:: create_many_to_many_relationship(relationship: PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata, *, solution: Optional[str] = None) -> None - - Add a many-to-many relationship creation to the batch. - - :param relationship: Relationship metadata. - :type relationship: ~PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata - :param solution: Optional solution unique name. - :type solution: str or None - - - - .. py:method:: delete_relationship(relationship_id: str) -> None - - Add a relationship-delete operation to the batch. - - :param relationship_id: GUID of the relationship metadata to delete. - :type relationship_id: :class:`str` - - - - .. py:method:: get_relationship(schema_name: str) -> None - - Add a relationship-metadata-get operation to the batch. - - The response will be in ``BatchItemResponse.data`` after execute. - - :param schema_name: Schema name of the relationship. - :type schema_name: :class:`str` - - - - .. py:method:: create_lookup_field(referencing_table: str, lookup_field_name: str, referenced_table: str, *, display_name: Optional[str] = None, description: Optional[str] = None, required: bool = False, cascade_delete: str = CASCADE_BEHAVIOR_REMOVE_LINK, solution: Optional[str] = None, language_code: int = 1033) -> None - - Add a lookup field creation to the batch (convenience wrapper for - :meth:`create_one_to_many_relationship`). - - :param referencing_table: Logical name of the child (many) table. - :type referencing_table: :class:`str` - :param lookup_field_name: Schema name for the lookup field. - :type lookup_field_name: :class:`str` - :param referenced_table: Logical name of the parent (one) table. - :type referenced_table: :class:`str` - :param display_name: Display name for the lookup field. - :type display_name: str or None - :param description: Optional description. - :type description: str or None - :param required: Whether the lookup is required. - :type required: :class:`bool` - :param cascade_delete: Delete cascade behaviour. - :type cascade_delete: :class:`str` - :param solution: Optional solution unique name. - :type solution: str or None - :param language_code: Language code for labels (default 1033). - :type language_code: :class:`int` - - - -.. py:class:: BatchQueryOperations(batch: _BatchContext) - - Query operations on a :class:`BatchRequest`. - - Mirrors ``client.query`` exactly: same method names, same signatures. - All methods return ``None``; results arrive via - :class:`~PowerPlatform.Dataverse.models.batch.BatchResult`. - - Do not instantiate directly; use ``batch.query``. - - - .. py:method:: sql(sql: str) -> None - - Add a SQL SELECT query to the batch. - - Mirrors :meth:`~PowerPlatform.Dataverse.operations.query.QueryOperations.sql`. - The entity set is resolved from the table name in the SQL statement at - :meth:`BatchRequest.execute` time. - - :param sql: A single ``SELECT`` statement within the Dataverse-supported subset. - :type sql: :class:`str` - - :raises ~PowerPlatform.Dataverse.core.errors.ValidationError: - If ``sql`` is not a non-empty string. - - Example:: - - batch.query.sql("SELECT accountid, name FROM account WHERE name = 'Contoso'") - - - -.. py:class:: BatchDataFrameOperations(batch: _BatchContext) - - DataFrame-oriented wrappers for batch record operations. - - Provides :meth:`create`, :meth:`update`, and :meth:`delete` that accept - ``pandas.DataFrame`` / ``pandas.Series`` inputs and convert them to standard - dicts before enqueueing on the batch. This lets data-science callers feed - DataFrames directly into a batch without manual conversion. - - Accessed via ``batch.dataframe``. - - Example:: - - import pandas as pd - - batch = client.batch.new() - df = pd.DataFrame([ - {"name": "Contoso", "telephone1": "555-0100"}, - {"name": "Fabrikam", "telephone1": "555-0200"}, - ]) - batch.dataframe.create("account", df) - result = batch.execute() - - - .. py:method:: create(table: str, records: pandas.DataFrame) -> None - - Enqueue record creates from a pandas DataFrame. - - Each row becomes a record. All rows are bundled in a single - ``CreateMultiple`` batch item (one HTTP request in the batch). - - :param table: Table schema name (e.g. ``"account"``). - :type table: :class:`str` - :param records: DataFrame where each row is a record to create. - :type records: ~pandas.DataFrame - - :raises TypeError: If ``records`` is not a pandas DataFrame. - :raises ValueError: If ``records`` is empty or any row has no non-null values. - - Example:: - - df = pd.DataFrame([{"name": "Contoso"}, {"name": "Fabrikam"}]) - batch.dataframe.create("account", df) - - - - .. py:method:: update(table: str, changes: pandas.DataFrame, id_column: str, clear_nulls: bool = False) -> None - - Enqueue record updates from a pandas DataFrame. - - Each row represents an update. The ``id_column`` specifies which - column contains the record GUIDs. - - :param table: Table schema name (e.g. ``"account"``). - :type table: :class:`str` - :param changes: DataFrame where each row contains a record GUID and - the fields to update. - :type changes: ~pandas.DataFrame - :param id_column: Name of the DataFrame column containing record GUIDs. - :type id_column: :class:`str` - :param clear_nulls: When ``False`` (default), NaN/None values are - skipped. When ``True``, NaN/None sends ``null`` to clear the field. - :type clear_nulls: :class:`bool` - - :raises TypeError: If ``changes`` is not a pandas DataFrame. - :raises ValueError: If ``changes`` is empty, ``id_column`` is missing, - or IDs are invalid. - - Example:: - - df = pd.DataFrame([ - {"accountid": "guid-1", "telephone1": "555-0100"}, - {"accountid": "guid-2", "telephone1": "555-0200"}, - ]) - batch.dataframe.update("account", df, id_column="accountid") - - - - .. py:method:: delete(table: str, ids: pandas.Series, use_bulk_delete: bool = True) -> None - - Enqueue record deletes from a pandas Series of GUIDs. - - :param table: Table schema name (e.g. ``"account"``). - :type table: :class:`str` - :param ids: Series of record GUIDs to delete. - :type ids: ~pandas.Series - :param use_bulk_delete: When ``True`` (default) and ``ids`` has multiple - values, use the ``BulkDelete`` action. - :type use_bulk_delete: :class:`bool` - - :raises TypeError: If ``ids`` is not a pandas Series. - :raises ValueError: If ``ids`` contains invalid values. - - Example:: - - ids_series = pd.Series(["guid-1", "guid-2", "guid-3"]) - batch.dataframe.delete("account", ids_series) - - - -.. py:class:: BatchRequest(client: PowerPlatform.Dataverse.client.DataverseClient) - - Builder for constructing and executing a Dataverse OData ``$batch`` request. - - Obtain via :meth:`BatchOperations.new` (``client.batch.new()``). Add operations - through ``records``, ``tables``, ``query``, and ``dataframe``, - optionally group writes - into a :meth:`changeset`, then call :meth:`execute`. - - Operations are executed sequentially in the order added. The resulting - :class:`~PowerPlatform.Dataverse.models.batch.BatchResult` contains one - :class:`~PowerPlatform.Dataverse.models.batch.BatchItemResponse` per HTTP - request dispatched (some operations expand to multiple requests). - - .. note:: - Maximum 1000 HTTP operations per batch. - - Example:: - - batch = client.batch.new() - batch.records.create("account", {"name": "Contoso"}) - batch.tables.get("account") - with batch.changeset() as cs: - ref = cs.records.create("contact", {"firstname": "Alice"}) - cs.records.update("account", account_id, { - "primarycontactid@odata.bind": ref - }) - result = batch.execute() - - - .. py:attribute:: records - - - .. py:attribute:: tables - - - .. py:attribute:: query - - - .. py:attribute:: dataframe - - - .. py:method:: changeset() -> ChangeSet - - Create a new :class:`ChangeSet` attached to this batch. - - The changeset is added to the batch immediately. Operations added to - the returned :class:`ChangeSet` via ``cs.records.*`` execute atomically. - - :returns: A new :class:`ChangeSet` ready to receive operations. - - Example:: - - with batch.changeset() as cs: - cs.records.create("account", {"name": "ACME"}) - cs.records.create("contact", {"firstname": "Bob"}) - - - - .. py:method:: execute(*, continue_on_error: bool = False) -> PowerPlatform.Dataverse.models.batch.BatchResult - - Submit the batch to Dataverse and return all responses. - - :param continue_on_error: When False (default), Dataverse stops at the - first failure and returns that operation's error as a 4xx response. - When True, ``Prefer: odata.continue-on-error`` is sent and all - operations are attempted. - :returns: :class:`~PowerPlatform.Dataverse.models.batch.BatchResult` - with one entry per HTTP operation in submission order. - :raises ValidationError: If the batch exceeds 1000 operations or an - unsupported column type is specified. - :raises MetadataError: If metadata pre-resolution fails (table or - column not found) for ``tables.delete``, ``tables.add_columns``, - or ``tables.remove_columns``. - :raises HttpError: On HTTP-level failures (auth, server error, etc.) - that prevent the batch from executing. - - - -.. py:class:: BatchOperations(client: PowerPlatform.Dataverse.client.DataverseClient) - - Namespace for batch operations (``client.batch``). - - Accessed via ``client.batch``. Use :meth:`new` to create a - :class:`BatchRequest` builder. - - :param client: The parent :class:`~PowerPlatform.Dataverse.client.DataverseClient` instance. - - Example:: - - batch = client.batch.new() - batch.records.create("account", {"name": "Fabrikam"}) - result = batch.execute() - - - .. py:method:: new() -> BatchRequest - - Create a new empty :class:`BatchRequest` builder. - - :returns: An empty :class:`BatchRequest`. - - - diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/dataframe/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/dataframe/index.rst.txt deleted file mode 100644 index d240dee2..00000000 --- a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/dataframe/index.rst.txt +++ /dev/null @@ -1,279 +0,0 @@ -PowerPlatform.Dataverse.operations.dataframe -============================================ - -.. py:module:: PowerPlatform.Dataverse.operations.dataframe - -.. autoapi-nested-parse:: - - DataFrame CRUD operations namespace for the Dataverse SDK. - - - -Classes -------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.operations.dataframe.DataFrameOperations - - -Module Contents ---------------- - -.. py:class:: DataFrameOperations(client: PowerPlatform.Dataverse.client.DataverseClient) - - Namespace for pandas DataFrame CRUD operations. - - Accessed via ``client.dataframe``. Provides DataFrame-oriented wrappers - around the record-level CRUD operations. - - :param client: The parent :class:`~PowerPlatform.Dataverse.client.DataverseClient` instance. - :type client: ~PowerPlatform.Dataverse.client.DataverseClient - - Example:: - - import pandas as pd - - client = DataverseClient(base_url, credential) - - # Query records as a DataFrame - df = client.dataframe.get("account", select=["name"], top=100) - - # Create records from a DataFrame - new_df = pd.DataFrame([{"name": "Contoso"}, {"name": "Fabrikam"}]) - new_df["accountid"] = client.dataframe.create("account", new_df) - - # Update records - new_df["telephone1"] = ["555-0100", "555-0200"] - client.dataframe.update("account", new_df, id_column="accountid") - - # Delete records - client.dataframe.delete("account", new_df["accountid"]) - - - .. py:method:: sql(sql: str) -> pandas.DataFrame - - Execute a SQL query and return the results as a pandas DataFrame. - - Delegates to :meth:`~PowerPlatform.Dataverse.operations.query.QueryOperations.sql` - and converts the list of records into a single DataFrame. - - :param sql: Supported SQL SELECT statement. - :type sql: :class:`str` - - :return: DataFrame containing all result rows. Returns an empty - DataFrame when no rows match. - :rtype: ~pandas.DataFrame - - :raises ~PowerPlatform.Dataverse.core.errors.ValidationError: - If ``sql`` is not a string or is empty. - - .. rubric:: Example - - SQL query to DataFrame:: - - df = client.dataframe.sql( - "SELECT TOP 100 name, revenue FROM account " - "WHERE statecode = 0 ORDER BY revenue" - ) - print(f"Got {len(df)} rows") - print(df.head()) - - Aggregate query to DataFrame:: - - df = client.dataframe.sql( - "SELECT a.name, COUNT(c.contactid) as cnt " - "FROM account a " - "JOIN contact c ON a.accountid = c.parentcustomerid " - "GROUP BY a.name" - ) - - - - .. py:method:: get(table: str, record_id: Optional[str] = None, select: Optional[List[str]] = None, filter: Optional[str] = None, orderby: Optional[List[str]] = None, top: Optional[int] = None, expand: Optional[List[str]] = None, page_size: Optional[int] = None, count: bool = False, include_annotations: Optional[str] = None) -> pandas.DataFrame - - Fetch records and return as a single pandas DataFrame. - - When ``record_id`` is provided, returns a single-row DataFrame. - When ``record_id`` is None, internally iterates all pages and returns one - consolidated DataFrame. - - :param table: Schema name of the table (e.g. ``"account"`` or ``"new_MyTestTable"``). - :type table: :class:`str` - :param record_id: Optional GUID to fetch a specific record. If None, queries multiple records. - :type record_id: :class:`str` or None - :param select: Optional list of attribute logical names to retrieve. - :type select: list[str] or None - :param filter: Optional OData filter string. Column names must use exact lowercase logical names. - :type filter: :class:`str` or None - :param orderby: Optional list of attributes to sort by. - :type orderby: list[str] or None - :param top: Optional maximum number of records to return. - :type top: :class:`int` or None - :param expand: Optional list of navigation properties to expand (case-sensitive). - :type expand: list[str] or None - :param page_size: Optional number of records per page for pagination. - :type page_size: :class:`int` or None - :param count: If ``True``, adds ``$count=true`` to include a total - record count in the response. - :type count: :class:`bool` - :param include_annotations: OData annotation pattern for the - ``Prefer: odata.include-annotations`` header (e.g. ``"*"`` or - ``"OData.Community.Display.V1.FormattedValue"``), or ``None``. - :type include_annotations: :class:`str` or None - - :return: DataFrame containing all matching records. Returns an empty DataFrame - when no records match. - :rtype: ~pandas.DataFrame - - :raises ValueError: If ``record_id`` is not a non-empty string, or if - query parameters (``filter``, ``orderby``, ``top``, ``expand``, - ``page_size``) are provided alongside ``record_id``. - - .. tip:: - For large tables, use ``top`` or ``filter`` to limit the result set. - - .. rubric:: Example - - Fetch a single record as a DataFrame:: - - df = client.dataframe.get("account", record_id=account_id, select=["name", "telephone1"]) - print(df) - - Query with filtering:: - - df = client.dataframe.get("account", filter="statecode eq 0", select=["name"]) - print(f"Got {len(df)} active accounts") - - Limit result size:: - - df = client.dataframe.get("account", select=["name"], top=100) - - - - .. py:method:: create(table: str, records: pandas.DataFrame) -> pandas.Series - - Create records from a pandas DataFrame. - - :param table: Schema name of the table (e.g. ``"account"`` or ``"new_MyTestTable"``). - :type table: :class:`str` - :param records: DataFrame where each row is a record to create. - :type records: ~pandas.DataFrame - - :return: Series of created record GUIDs, aligned with the input DataFrame index. - :rtype: ~pandas.Series - - :raises TypeError: If ``records`` is not a pandas DataFrame. - :raises ValueError: If ``records`` is empty or the number of returned - IDs does not match the number of input rows. - - .. tip:: - All rows are sent in a single ``CreateMultiple`` request. For very - large DataFrames, consider splitting into smaller batches to avoid - request timeouts. - - .. rubric:: Example - - Create records from a DataFrame:: - - import pandas as pd - - df = pd.DataFrame([ - {"name": "Contoso", "telephone1": "555-0100"}, - {"name": "Fabrikam", "telephone1": "555-0200"}, - ]) - df["accountid"] = client.dataframe.create("account", df) - - - - .. py:method:: update(table: str, changes: pandas.DataFrame, id_column: str, clear_nulls: bool = False) -> None - - Update records from a pandas DataFrame. - - Each row in the DataFrame represents an update. The ``id_column`` specifies which - column contains the record GUIDs. - - :param table: Schema name of the table (e.g. ``"account"`` or ``"new_MyTestTable"``). - :type table: :class:`str` - :param changes: DataFrame where each row contains a record GUID and the fields to update. - :type changes: ~pandas.DataFrame - :param id_column: Name of the DataFrame column containing record GUIDs. - :type id_column: :class:`str` - :param clear_nulls: When ``False`` (default), missing values (NaN/None) are skipped - (the field is left unchanged on the server). When ``True``, missing values are sent - as ``null`` to Dataverse, clearing the field. Use ``True`` only when you intentionally - want NaN/None values to clear fields. - :type clear_nulls: :class:`bool` - - :raises TypeError: If ``changes`` is not a pandas DataFrame. - :raises ValueError: If ``changes`` is empty, ``id_column`` is not found in the - DataFrame, ``id_column`` contains invalid (non-string, empty, or whitespace-only) - values, or no updatable columns exist besides ``id_column``. - When ``clear_nulls`` is ``False`` (default), rows where all change values - are NaN/None produce empty patches and are silently skipped. If all rows - are skipped, the method returns without making an API call. When - ``clear_nulls`` is ``True``, NaN/None values become explicit nulls, so - rows are never skipped. - - .. tip:: - All rows are sent in a single ``UpdateMultiple`` request (or a - single PATCH for one row). For very large DataFrames, consider - splitting into smaller batches to avoid request timeouts. - - .. rubric:: Example - - Update records with different values per row:: - - import pandas as pd - - df = pd.DataFrame([ - {"accountid": "guid-1", "telephone1": "555-0100"}, - {"accountid": "guid-2", "telephone1": "555-0200"}, - ]) - client.dataframe.update("account", df, id_column="accountid") - - Broadcast the same change to all records:: - - df = pd.DataFrame({"accountid": ["guid-1", "guid-2", "guid-3"]}) - df["websiteurl"] = "https://example.com" - client.dataframe.update("account", df, id_column="accountid") - - Clear a field by setting clear_nulls=True:: - - df = pd.DataFrame([{"accountid": "guid-1", "websiteurl": None}]) - client.dataframe.update("account", df, id_column="accountid", clear_nulls=True) - - - - .. py:method:: delete(table: str, ids: pandas.Series, use_bulk_delete: bool = True) -> Optional[str] - - Delete records by passing a pandas Series of GUIDs. - - :param table: Schema name of the table (e.g. ``"account"`` or ``"new_MyTestTable"``). - :type table: :class:`str` - :param ids: Series of record GUIDs to delete. - :type ids: ~pandas.Series - :param use_bulk_delete: When ``True`` (default) and ``ids`` contains multiple values, execute the BulkDelete - action and return its async job identifier. When ``False`` each record is deleted sequentially. - :type use_bulk_delete: :class:`bool` - - :raises TypeError: If ``ids`` is not a pandas Series. - :raises ValueError: If ``ids`` contains invalid (non-string, empty, or - whitespace-only) values. - - :return: BulkDelete job ID when deleting multiple records via BulkDelete; - ``None`` when deleting a single record, using sequential deletion, or - when ``ids`` is empty. - :rtype: :class:`str` or None - - .. rubric:: Example - - Delete records using a Series:: - - import pandas as pd - - ids = pd.Series(["guid-1", "guid-2", "guid-3"]) - client.dataframe.delete("account", ids) - - - diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/files/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/files/index.rst.txt deleted file mode 100644 index ad07ef55..00000000 --- a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/files/index.rst.txt +++ /dev/null @@ -1,99 +0,0 @@ -PowerPlatform.Dataverse.operations.files -======================================== - -.. py:module:: PowerPlatform.Dataverse.operations.files - -.. autoapi-nested-parse:: - - File operations namespace for the Dataverse SDK. - - - -Classes -------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.operations.files.FileOperations - - -Module Contents ---------------- - -.. py:class:: FileOperations(client: PowerPlatform.Dataverse.client.DataverseClient) - - Namespace for file operations. - - Accessed via ``client.files``. Provides file upload operations for - Dataverse file columns. - - :param client: The parent :class:`~PowerPlatform.Dataverse.client.DataverseClient` instance. - :type client: ~PowerPlatform.Dataverse.client.DataverseClient - - Example:: - - client = DataverseClient(base_url, credential) - - client.files.upload( - "account", account_id, "new_Document", "/path/to/file.pdf" - ) - - - .. py:method:: upload(table: str, record_id: str, file_column: str, path: str, *, mode: Optional[str] = None, mime_type: Optional[str] = None, if_none_match: bool = True) -> None - - Upload a file to a Dataverse file column. - - :param table: Schema name of the table (e.g. ``"account"`` or - ``"new_MyTestTable"``). - :type table: :class:`str` - :param record_id: GUID of the target record. - :type record_id: :class:`str` - :param file_column: Schema name of the file column attribute (e.g., - ``"new_Document"``). If the column doesn't exist, it will be - created automatically. - :type file_column: :class:`str` - :param path: Local filesystem path to the file. The stored filename - will be the basename of this path. - :type path: :class:`str` - :param mode: Upload strategy: ``"auto"`` (default), ``"small"``, or - ``"chunk"``. Auto mode selects small or chunked upload based on - file size. - :type mode: :class:`str` or None - :param mime_type: Explicit MIME type to store with the file (e.g. - ``"application/pdf"``). If not provided, defaults to - ``"application/octet-stream"``. - :type mime_type: :class:`str` or None - :param if_none_match: When True (default), sends - ``If-None-Match: null`` header to only succeed if the column is - currently empty. Set False to always overwrite using - ``If-Match: *``. - :type if_none_match: :class:`bool` - - :raises ~PowerPlatform.Dataverse.core.errors.HttpError: - If the upload fails or the file column is not empty when - ``if_none_match=True``. - :raises FileNotFoundError: If the specified file path does not exist. - - .. rubric:: Example - - Upload a PDF file:: - - client.files.upload( - "account", - account_id, - "new_Contract", - "/path/to/contract.pdf", - mime_type="application/pdf", - ) - - Upload with auto mode selection:: - - client.files.upload( - "email", - email_id, - "new_Attachment", - "/path/to/large_file.zip", - ) - - - diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/index.rst.txt deleted file mode 100644 index cbb14381..00000000 --- a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/index.rst.txt +++ /dev/null @@ -1,28 +0,0 @@ -PowerPlatform.Dataverse.operations -================================== - -.. py:module:: PowerPlatform.Dataverse.operations - -.. autoapi-nested-parse:: - - Operation namespaces for the Dataverse SDK. - - This module contains the operation namespace classes that organize - SDK operations into logical groups: records, query, and tables. - - - -Submodules ----------- - -.. toctree:: - :maxdepth: 1 - - /autoapi/PowerPlatform/Dataverse/operations/batch/index - /autoapi/PowerPlatform/Dataverse/operations/dataframe/index - /autoapi/PowerPlatform/Dataverse/operations/files/index - /autoapi/PowerPlatform/Dataverse/operations/query/index - /autoapi/PowerPlatform/Dataverse/operations/records/index - /autoapi/PowerPlatform/Dataverse/operations/tables/index - - diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/query/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/query/index.rst.txt deleted file mode 100644 index 740ab5b4..00000000 --- a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/query/index.rst.txt +++ /dev/null @@ -1,345 +0,0 @@ -PowerPlatform.Dataverse.operations.query -======================================== - -.. py:module:: PowerPlatform.Dataverse.operations.query - -.. autoapi-nested-parse:: - - Query operations namespace for the Dataverse SDK. - - - -Classes -------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.operations.query.QueryOperations - - -Module Contents ---------------- - -.. py:class:: QueryOperations(client: PowerPlatform.Dataverse.client.DataverseClient) - - Namespace for query operations. - - Accessed via ``client.query``. Provides query and search operations - against Dataverse tables. - - :param client: The parent :class:`~PowerPlatform.Dataverse.client.DataverseClient` instance. - :type client: ~PowerPlatform.Dataverse.client.DataverseClient - - Example:: - - from PowerPlatform.Dataverse.models.filters import col - - client = DataverseClient(base_url, credential) - - # Fluent query builder (recommended) - for record in (client.query.builder("account") - .select("name", "revenue") - .where(col("statecode") == 0) - .order_by("revenue", descending=True) - .top(100) - .execute()): - print(record["name"]) - - # SQL query - rows = client.query.sql("SELECT TOP 10 name FROM account ORDER BY name") - for row in rows: - print(row["name"]) - - - .. py:method:: builder(table: str) -> PowerPlatform.Dataverse.models.query_builder.QueryBuilder - - Create a fluent query builder for the specified table. - - Returns a :class:`~PowerPlatform.Dataverse.models.query_builder.QueryBuilder` - that can be chained with filter, select, and order methods, then - executed directly via ``.execute()``. - - :param table: Table schema name (e.g. ``"account"``). - :type table: :class:`str` - :return: A QueryBuilder instance bound to this client. - :rtype: ~PowerPlatform.Dataverse.models.query_builder.QueryBuilder - - .. rubric:: Example - - Build and execute a query fluently:: - - from PowerPlatform.Dataverse.models.filters import col - - for record in (client.query.builder("account") - .select("name", "revenue") - .where(col("statecode") == 0) - .where(col("revenue") > 1_000_000) - .order_by("revenue", descending=True) - .top(100) - .page_size(50) - .execute()): - print(record["name"]) - - With composable expression tree:: - - from PowerPlatform.Dataverse.models.filters import col - - for record in (client.query.builder("account") - .where((col("statecode") == 0) | (col("statecode") == 1)) - .where(col("revenue") > 100_000) - .execute()): - print(record["name"]) - - - - .. py:method:: sql(sql: str) -> List[PowerPlatform.Dataverse.models.record.Record] - - Execute a read-only SQL query using the Dataverse Web API. - - The Dataverse SQL endpoint supports a broad subset of T-SQL:: - - SELECT / SELECT DISTINCT / SELECT TOP N (0-5000) - FROM table [alias] - INNER JOIN / LEFT JOIN (multi-table, no depth limit) - WHERE (=, !=, >, <, >=, <=, LIKE, IN, NOT IN, IS NULL, - IS NOT NULL, BETWEEN, AND, OR, nested parentheses) - GROUP BY column - ORDER BY column [ASC|DESC] - OFFSET n ROWS FETCH NEXT m ROWS ONLY - COUNT(*), SUM(), AVG(), MIN(), MAX() - - ``SELECT *`` is not supported -- specify column names explicitly. - Use :meth:`sql_columns` to discover available column names for a table. - - Not supported: SELECT *, subqueries, CTE, HAVING, UNION, - RIGHT/FULL/CROSS JOIN, CASE, COALESCE, window functions, - string/date/math functions, INSERT/UPDATE/DELETE. For writes, use - ``client.records`` methods. - - :param sql: Supported SQL SELECT statement. - :type sql: :class:`str` - - :return: List of :class:`~PowerPlatform.Dataverse.models.record.Record` - objects. Returns an empty list when no rows match. - :rtype: list[~PowerPlatform.Dataverse.models.record.Record] - - :raises ~PowerPlatform.Dataverse.core.errors.ValidationError: - If ``sql`` is not a string or is empty. - - .. rubric:: Example - - Basic query:: - - rows = client.query.sql( - "SELECT TOP 10 name FROM account ORDER BY name" - ) - - JOIN with aggregation:: - - rows = client.query.sql( - "SELECT a.name, COUNT(c.contactid) as cnt " - "FROM account a " - "JOIN contact c ON a.accountid = c.parentcustomerid " - "GROUP BY a.name" - ) - - - - .. py:method:: fetchxml(xml: str) -> PowerPlatform.Dataverse.models.fetchxml_query.FetchXmlQuery - - Return an inert :class:`~PowerPlatform.Dataverse.models.fetchxml_query.FetchXmlQuery` object. - - No HTTP request is made until - :meth:`~PowerPlatform.Dataverse.models.fetchxml_query.FetchXmlQuery.execute` - or - :meth:`~PowerPlatform.Dataverse.models.fetchxml_query.FetchXmlQuery.execute_pages` - is called on the returned object. - - Use for SQL-JOIN scenarios, aggregate queries, or other operations that - the OData builder endpoint cannot express. - - :param xml: Well-formed FetchXML query string. The root ```` - element determines the entity set endpoint. - :type xml: :class:`str` - :return: Inert query object with ``.execute()`` and ``.execute_pages()`` methods. - :rtype: :class:`~PowerPlatform.Dataverse.models.fetchxml_query.FetchXmlQuery` - :raises ValueError: If the FetchXML is missing a root ```` element - or the entity ``name`` attribute. - - Example:: - - query = client.query.fetchxml(""" - - - - - - - - - """) - - # Eager — collect all pages: - result = query.execute() - df = result.to_dataframe() - - # Lazy — process one page at a time: - for page in query.execute_pages(): - process(page.to_dataframe()) - - - - .. py:method:: sql_columns(table: str, *, include_system: bool = False) -> List[Dict[str, Any]] - - Return a simplified list of SQL-usable columns for a table. - - Each dict contains ``name`` (logical name for SQL), ``type`` - (Dataverse attribute type), ``is_pk`` (primary key flag), and - ``label`` (display name). Virtual columns are always excluded - because the SQL endpoint cannot query them. - - :param table: Schema name of the table (e.g. ``"account"``). - :type table: :class:`str` - :param include_system: When ``False`` (default), columns that end - with common system suffixes (``_base``, ``versionnumber``, - ``timezoneruleversionnumber``, ``utcconversiontimezonecode``, - ``importsequencenumber``, ``overriddencreatedon``) are excluded. - :type include_system: :class:`bool` - - :return: List of column metadata dicts. - :rtype: list[dict[str, typing.Any]] - - Example:: - - cols = client.query.sql_columns("account") - for c in cols: - print(f"{c['name']:30s} {c['type']:20s} PK={c['is_pk']}") - - - - .. py:method:: odata_select(table: str, *, include_system: bool = False) -> List[str] - - Return a list of column logical names suitable for ``$select``. - - Can be passed directly to ``client.records.get(table, select=...)``. - - :param table: Schema name of the table (e.g. ``"account"``). - :type table: :class:`str` - :param include_system: Include system columns (default ``False``). - :type include_system: :class:`bool` - - :return: List of lowercase column logical names. - :rtype: list[str] - - Example:: - - cols = client.query.odata_select("account") - for page in client.records.get("account", select=cols, top=10): - for r in page: - print(r) - - - - .. py:method:: odata_expands(table: str) -> List[Dict[str, Any]] - - Discover all ``$expand`` navigation properties from a table. - - Returns entries for each outgoing lookup (single-valued navigation - property). Each entry contains the exact PascalCase navigation - property name needed for ``$expand`` and ``@odata.bind``, plus - the target entity set name. - - :param table: Schema name of the table (e.g. ``"contact"``). - :type table: :class:`str` - - :return: List of dicts, each with: - - - ``nav_property`` -- PascalCase navigation property for $expand - - ``target_table`` -- target entity logical name - - ``target_entity_set`` -- target entity set (for @odata.bind) - - ``lookup_attribute`` -- the lookup column logical name - - ``relationship`` -- relationship schema name - - :rtype: list[dict[str, typing.Any]] - - Example:: - - expands = client.query.odata_expands("contact") - for e in expands: - print(f"expand={e['nav_property']} -> {e['target_table']}") - - # Use in a query - e = next(e for e in expands if e['target_table'] == 'account') - for page in client.records.get("contact", - select=["fullname"], - expand=[e['nav_property']]): - ... - - - - .. py:method:: odata_expand(from_table: str, to_table: str) -> str - - Return the navigation property name to ``$expand`` from one table to another. - - Discovers via relationship metadata. Returns the exact PascalCase - string for the ``expand=`` parameter. - - :param from_table: Schema name of the source table (e.g. ``"contact"``). - :type from_table: :class:`str` - :param to_table: Schema name of the target table (e.g. ``"account"``). - :type to_table: :class:`str` - - :return: The navigation property name (PascalCase). - :rtype: :class:`str` - - :raises ValueError: If no navigation property found for the target. - - Example:: - - nav = client.query.odata_expand("contact", "account") - # Returns e.g. "parentcustomerid_account" - for page in client.records.get("contact", - select=["fullname"], - expand=[nav], - top=5): - for r in page: - acct = r.get(nav) or {} - print(f"{r['fullname']} -> {acct.get('name', 'N/A')}") - - - - .. py:method:: odata_bind(from_table: str, to_table: str, target_id: str) -> Dict[str, str] - - Build an ``@odata.bind`` entry for setting a lookup field. - - Auto-discovers the navigation property name and entity set name - from metadata. Returns a single-entry dict that can be merged - into a create or update payload. - - :param from_table: Schema name of the entity being created/updated. - :type from_table: :class:`str` - :param to_table: Schema name of the target entity the lookup points to. - :type to_table: :class:`str` - :param target_id: GUID of the target record. - :type target_id: :class:`str` - - :return: A dict like ``{"NavProp@odata.bind": "/entityset(guid)"}``. - :rtype: dict[str, str] - - :raises ValueError: If no relationship found between the tables. - - Example:: - - # Instead of manually constructing: - # {"parentcustomerid_account@odata.bind": "/accounts(guid)"} - # Just do: - bind = client.query.odata_bind("contact", "account", acct_id) - client.records.create("contact", { - "firstname": "Jane", - "lastname": "Doe", - **bind, - }) - - - diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/records/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/records/index.rst.txt deleted file mode 100644 index b76711dd..00000000 --- a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/records/index.rst.txt +++ /dev/null @@ -1,461 +0,0 @@ -PowerPlatform.Dataverse.operations.records -========================================== - -.. py:module:: PowerPlatform.Dataverse.operations.records - -.. autoapi-nested-parse:: - - Record CRUD operations namespace for the Dataverse SDK. - - - -Classes -------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.operations.records.RecordOperations - - -Module Contents ---------------- - -.. py:class:: RecordOperations(client: PowerPlatform.Dataverse.client.DataverseClient) - - Namespace for record-level CRUD operations. - - Accessed via ``client.records``. Provides create, update, delete, and get - operations on individual Dataverse records. - - :param client: The parent :class:`~PowerPlatform.Dataverse.client.DataverseClient` instance. - :type client: ~PowerPlatform.Dataverse.client.DataverseClient - - Example:: - - client = DataverseClient(base_url, credential) - - # Create a single record - guid = client.records.create("account", {"name": "Contoso Ltd"}) - - # Get a record - record = client.records.get("account", guid, select=["name"]) - - # Update a record - client.records.update("account", guid, {"telephone1": "555-0100"}) - - # Delete a record - client.records.delete("account", guid) - - - .. py:method:: create(table: str, data: Dict[str, Any]) -> str - create(table: str, data: List[Dict[str, Any]]) -> List[str] - - Create one or more records in a Dataverse table. - - When ``data`` is a single dictionary, creates one record and returns its - GUID as a string. When ``data`` is a list of dictionaries, creates all - records via the ``CreateMultiple`` action and returns a list of GUIDs. - - :param table: Schema name of the table (e.g. ``"account"`` or ``"new_MyTestTable"``). - :type table: :class:`str` - :param data: A single record dictionary or a list of record dictionaries. - Each dictionary maps column schema names to values. - :type data: dict or list[dict] - - :return: A single GUID string for a single record, or a list of GUID - strings for bulk creation. - :rtype: str or list[str] - - :raises TypeError: If ``data`` is not a dict or list[dict]. - - .. rubric:: Example - - Create a single record:: - - guid = client.records.create("account", {"name": "Contoso"}) - print(f"Created: {guid}") - - Create multiple records:: - - guids = client.records.create("account", [ - {"name": "Contoso"}, - {"name": "Fabrikam"}, - ]) - print(f"Created {len(guids)} accounts") - - - - .. py:method:: update(table: str, ids: Union[str, List[str]], changes: Union[Dict[str, Any], List[Dict[str, Any]]]) -> None - - Update one or more records in a Dataverse table. - - Supports three usage patterns: - - 1. **Single** -- ``update("account", "guid", {"name": "New"})`` - 2. **Broadcast** -- ``update("account", [id1, id2], {"status": 1})`` - applies the same changes dict to every ID. - 3. **Paired** -- ``update("account", [id1, id2], [ch1, ch2])`` - applies each changes dict to its corresponding ID (lists must be - equal length). - - :param table: Schema name of the table (e.g. ``"account"``). - :type table: :class:`str` - :param ids: A single GUID string, or a list of GUID strings. - :type ids: str or list[str] - :param changes: A dictionary of field changes (single/broadcast), or a - list of dictionaries (paired, one per ID). - :type changes: dict or list[dict] - - :raises TypeError: If ``ids`` is not str or list[str], or if ``changes`` - does not match the expected pattern. - - .. rubric:: Example - - Single update:: - - client.records.update("account", account_id, {"telephone1": "555-0100"}) - - Broadcast update:: - - client.records.update("account", [id1, id2], {"statecode": 1}) - - Paired update:: - - client.records.update( - "account", - [id1, id2], - [{"name": "Name A"}, {"name": "Name B"}], - ) - - - - .. py:method:: delete(table: str, ids: str) -> None - delete(table: str, ids: List[str], *, use_bulk_delete: bool = True) -> Optional[str] - - Delete one or more records from a Dataverse table. - - When ``ids`` is a single string, deletes that one record. When ``ids`` - is a list, either executes a BulkDelete action (returning the async job - ID) or deletes each record sequentially depending on ``use_bulk_delete``. - - :param table: Schema name of the table (e.g. ``"account"``). - :type table: :class:`str` - :param ids: A single GUID string, or a list of GUID strings. - :type ids: str or list[str] - :param use_bulk_delete: When True (default) and ``ids`` is a list, use - the BulkDelete action and return its async job ID. When False, delete - records one at a time. - :type use_bulk_delete: :class:`bool` - - :return: The BulkDelete job ID when bulk-deleting; otherwise None. - :rtype: :class:`str` or None - - :raises TypeError: If ``ids`` is not str or list[str]. - - .. rubric:: Example - - Delete a single record:: - - client.records.delete("account", account_id) - - Bulk delete:: - - job_id = client.records.delete("account", [id1, id2, id3]) - - - - .. py:method:: get(table: str, record_id: str, *, select: Optional[List[str]] = None) -> PowerPlatform.Dataverse.models.record.Record - get(table: str, *, select: Optional[List[str]] = None, filter: Optional[str] = None, orderby: Optional[List[str]] = None, top: Optional[int] = None, expand: Optional[List[str]] = None, page_size: Optional[int] = None, count: bool = False, include_annotations: Optional[str] = None) -> Iterable[List[PowerPlatform.Dataverse.models.record.Record]] - - Fetch a single record by ID, or fetch multiple records with pagination. - - This method has two usage patterns: - - **Fetch a single record** -- ``get(table, record_id, *, select=...)`` - - Pass ``record_id`` as a positional argument to retrieve one record - and get back a :class:`dict`. Query parameters (``filter``, - ``orderby``, ``top``, ``expand``, ``page_size``) must not be provided. - - **Fetch multiple records** -- ``get(table, *, select=..., filter=..., ...)`` - - Omit ``record_id`` to perform a paginated fetch and get back a - generator that yields one page (list of record dicts) at a time. - Automatically follows ``@odata.nextLink`` for server-side paging. - - :param table: Schema name of the table (e.g. ``"account"`` or - ``"new_MyTestTable"``). - :type table: :class:`str` - :param record_id: GUID of the record to retrieve. When omitted, - performs a multi-record fetch instead. - :type record_id: :class:`str` or None - :param select: Optional list of column logical names to include. - Column names are automatically lowercased. - :type select: list[str] or None - :param filter: Optional OData ``$filter`` expression (e.g. - ``"name eq 'Contoso'"``). Column names in filter expressions must - use exact lowercase logical names. Only used for multi-record - queries. - :type filter: :class:`str` or None - :param orderby: Optional list of sort expressions (e.g. - ``["name asc", "createdon desc"]``). Column names are - automatically lowercased. Only used for multi-record queries. - :type orderby: list[str] or None - :param top: Optional maximum total number of records to return. Only - used for multi-record queries. - :type top: :class:`int` or None - :param expand: Optional list of navigation properties to expand (e.g. - ``["primarycontactid"]``). Case-sensitive; must match - server-defined names exactly. Only used for multi-record queries. - :type expand: list[str] or None - :param page_size: Optional per-page size hint sent via - ``Prefer: odata.maxpagesize``. Only used for multi-record queries. - :type page_size: :class:`int` or None - :param count: If ``True``, adds ``$count=true`` to include a total - record count in the response. Only used for multi-record queries. - :type count: :class:`bool` - :param include_annotations: OData annotation pattern for the - ``Prefer: odata.include-annotations`` header (e.g. ``"*"`` or - ``"OData.Community.Display.V1.FormattedValue"``), or ``None``. - Only used for multi-record queries. - :type include_annotations: :class:`str` or None - - :return: A single :class:`~PowerPlatform.Dataverse.models.record.Record` - when ``record_id`` is provided, or a generator yielding pages - (lists of :class:`~PowerPlatform.Dataverse.models.record.Record`) - when fetching multiple records. - :rtype: ~PowerPlatform.Dataverse.models.record.Record or - collections.abc.Iterable[list[~PowerPlatform.Dataverse.models.record.Record]] - - :raises TypeError: If ``record_id`` is provided but not a string. - :raises ValueError: If query parameters are provided alongside - ``record_id``. - - .. rubric:: Example - - Fetch a single record:: - - record = client.records.get( - "account", account_id, select=["name", "telephone1"] - ) - print(record["name"]) - - Fetch multiple records with pagination:: - - for page in client.records.get( - "account", - filter="statecode eq 0", - select=["name", "telephone1"], - page_size=50, - ): - for record in page: - print(record["name"]) - - - - .. py:method:: retrieve(table: str, record_id: str, *, select: Optional[List[str]] = None, expand: Optional[List[str]] = None, include_annotations: Optional[str] = None) -> Optional[PowerPlatform.Dataverse.models.record.Record] - - Fetch a single record by its GUID, returning ``None`` if not found. - - GA replacement for ``records.get(table, record_id)``. Returns ``None`` - instead of raising when the record does not exist (HTTP 404). - - :param table: Schema name of the table (e.g. ``"account"``). - :type table: :class:`str` - :param record_id: GUID of the record to retrieve. - :type record_id: :class:`str` - :param select: Optional list of column logical names to include. - :type select: list[str] or None - :param expand: Optional list of navigation properties to expand (e.g. - ``["primarycontactid"]``). Navigation property names are - case-sensitive and must match the entity's ``$metadata``. - :type expand: list[str] or None - :param include_annotations: OData annotation pattern for the - ``Prefer: odata.include-annotations`` header (e.g. ``"*"`` or - ``"OData.Community.Display.V1.FormattedValue"``), or ``None``. - :type include_annotations: :class:`str` or None - :return: Typed record, or ``None`` if not found. - :rtype: :class:`~PowerPlatform.Dataverse.models.record.Record` or None - - Example:: - - record = client.records.retrieve( - "account", account_id, - select=["name", "statuscode"], - expand=["primarycontactid"], - include_annotations="OData.Community.Display.V1.FormattedValue", - ) - if record is not None: - contact = record.get("primarycontactid") or {} - print(contact.get("fullname")) - - - - .. py:method:: list(table: str, *, filter: Optional[Union[str, PowerPlatform.Dataverse.models.filters.FilterExpression]] = None, select: Optional[List[str]] = None, orderby: Optional[List[str]] = None, top: Optional[int] = None, expand: Optional[List[str]] = None, page_size: Optional[int] = None, count: bool = False, include_annotations: Optional[str] = None) -> PowerPlatform.Dataverse.models.record.QueryResult - - Fetch multiple records and return them as a :class:`~PowerPlatform.Dataverse.models.record.QueryResult`. - - GA replacement for ``records.get(table, filter=...)``. All pages are - collected eagerly and returned as a single :class:`~PowerPlatform.Dataverse.models.record.QueryResult`. - - :param table: Schema name of the table (e.g. ``"account"``). - :type table: :class:`str` - :param filter: Optional OData filter string or :class:`~PowerPlatform.Dataverse.models.filters.FilterExpression`. - :type filter: str or FilterExpression or None - :param select: Optional list of column logical names to include. - :type select: list[str] or None - :param orderby: Optional list of sort expressions (e.g. ``["name asc", "createdon desc"]``). - :type orderby: list[str] or None - :param top: Maximum total number of records to return. - :type top: int or None - :param expand: Optional list of navigation properties to expand. - :type expand: list[str] or None - :param page_size: Per-page size hint via ``Prefer: odata.maxpagesize``. - :type page_size: int or None - :param count: If ``True``, adds ``$count=true`` to include a total record count. - :type count: bool - :param include_annotations: OData annotation pattern for the - ``Prefer: odata.include-annotations`` header, or ``None``. - :type include_annotations: :class:`str` or None - :return: All matching records collected into a :class:`~PowerPlatform.Dataverse.models.record.QueryResult`. - :rtype: :class:`~PowerPlatform.Dataverse.models.record.QueryResult` - - Example:: - - from PowerPlatform.Dataverse import col - - result = client.records.list( - "account", - filter=col("statecode") == 0, - select=["name", "statuscode"], - orderby=["name asc"], - top=100, - include_annotations="OData.Community.Display.V1.FormattedValue", - ) - for record in result: - print(record["name"], record.get("statuscode@OData.Community.Display.V1.FormattedValue")) - - - - .. py:method:: list_pages(table: str, *, filter: Optional[Union[str, PowerPlatform.Dataverse.models.filters.FilterExpression]] = None, select: Optional[List[str]] = None, orderby: Optional[List[str]] = None, top: Optional[int] = None, expand: Optional[List[str]] = None, page_size: Optional[int] = None, count: bool = False, include_annotations: Optional[str] = None) -> Iterator[PowerPlatform.Dataverse.models.record.QueryResult] - - Lazily yield one :class:`~PowerPlatform.Dataverse.models.record.QueryResult` per HTTP page. - - Streaming counterpart to :meth:`list`. Each iteration triggers one - network request via ``@odata.nextLink``. One-shot — do not iterate - more than once. - - :param table: Schema name of the table (e.g. ``"account"``). - :type table: :class:`str` - :param filter: Optional OData filter string or :class:`~PowerPlatform.Dataverse.models.filters.FilterExpression`. - :type filter: str or FilterExpression or None - :param select: Optional list of column logical names to include. - :type select: list[str] or None - :param orderby: Optional list of sort expressions (e.g. ``["name asc", "createdon desc"]``). - :type orderby: list[str] or None - :param top: Maximum total number of records to return. - :type top: int or None - :param expand: Optional list of navigation properties to expand. - :type expand: list[str] or None - :param page_size: Per-page size hint via ``Prefer: odata.maxpagesize``. - :type page_size: int or None - :param count: If ``True``, adds ``$count=true`` to include a total record count. - :type count: bool - :param include_annotations: OData annotation pattern for the - ``Prefer: odata.include-annotations`` header, or ``None``. - :type include_annotations: :class:`str` or None - :return: Iterator of per-page :class:`~PowerPlatform.Dataverse.models.record.QueryResult` objects. - :rtype: Iterator[:class:`~PowerPlatform.Dataverse.models.record.QueryResult`] - - Example:: - - for page in client.records.list_pages( - "account", - filter="statecode eq 0", - orderby=["name asc"], - page_size=200, - ): - process(page.to_dataframe()) - - - - .. py:method:: upsert(table: str, items: List[Union[PowerPlatform.Dataverse.models.upsert.UpsertItem, Dict[str, Any]]]) -> None - - Upsert one or more records identified by alternate keys. - - When ``items`` contains a single entry, performs a single upsert via PATCH - using the alternate key in the URL. When ``items`` contains multiple entries, - uses the ``UpsertMultiple`` bulk action. - - Each item must be either a :class:`~PowerPlatform.Dataverse.models.upsert.UpsertItem` - or a plain ``dict`` with ``"alternate_key"`` and ``"record"`` keys (both dicts). - - :param table: Schema name of the table (e.g. ``"account"`` or ``"new_MyTestTable"``). - :type table: str - :param items: Non-empty list of :class:`~PowerPlatform.Dataverse.models.upsert.UpsertItem` - instances or dicts with ``"alternate_key"`` and ``"record"`` keys. - :type items: list[UpsertItem | dict] - - :return: ``None`` - :rtype: None - - :raises TypeError: If ``items`` is not a non-empty list, or if any element is - neither a :class:`~PowerPlatform.Dataverse.models.upsert.UpsertItem` nor a - dict with ``"alternate_key"`` and ``"record"`` keys. - - .. rubric:: Example - - Upsert a single record using ``UpsertItem``:: - - from PowerPlatform.Dataverse.models import UpsertItem - - client.records.upsert("account", [ - UpsertItem( - alternate_key={"accountnumber": "ACC-001"}, - record={"name": "Contoso Ltd", "description": "Primary account"}, - ) - ]) - - Upsert a single record using a plain dict:: - - client.records.upsert("account", [ - { - "alternate_key": {"accountnumber": "ACC-001"}, - "record": {"name": "Contoso Ltd", "description": "Primary account"}, - }, - ]) - - Upsert multiple records using ``UpsertItem``:: - - from PowerPlatform.Dataverse.models import UpsertItem - - client.records.upsert("account", [ - UpsertItem( - alternate_key={"accountnumber": "ACC-001"}, - record={"name": "Contoso Ltd", "description": "Primary account"}, - ), - UpsertItem( - alternate_key={"accountnumber": "ACC-002"}, - record={"name": "Fabrikam Inc", "description": "Partner account"}, - ), - ]) - - Upsert multiple records using plain dicts:: - - client.records.upsert("account", [ - { - "alternate_key": {"accountnumber": "ACC-001"}, - "record": {"name": "Contoso Ltd", "description": "Primary account"}, - }, - { - "alternate_key": {"accountnumber": "ACC-002"}, - "record": {"name": "Fabrikam Inc", "description": "Partner account"}, - }, - ]) - - The ``alternate_key`` dict may contain multiple columns when the configured - alternate key is composite, e.g. - ``{"accountnumber": "ACC-001", "address1_postalcode": "98052"}``. - - - diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/tables/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/tables/index.rst.txt deleted file mode 100644 index 0cf63a2e..00000000 --- a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/operations/tables/index.rst.txt +++ /dev/null @@ -1,683 +0,0 @@ -PowerPlatform.Dataverse.operations.tables -========================================= - -.. py:module:: PowerPlatform.Dataverse.operations.tables - -.. autoapi-nested-parse:: - - Table metadata operations namespace for the Dataverse SDK. - - - -Classes -------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.operations.tables.TableOperations - - -Module Contents ---------------- - -.. py:class:: TableOperations(client: PowerPlatform.Dataverse.client.DataverseClient) - - Namespace for table-level metadata operations. - - Accessed via ``client.tables``. Provides operations to create, delete, - inspect, and list Dataverse tables, as well as add and remove columns. - - :param client: The parent :class:`~PowerPlatform.Dataverse.client.DataverseClient` instance. - :type client: ~PowerPlatform.Dataverse.client.DataverseClient - - Example:: - - client = DataverseClient(base_url, credential) - - # Create a table - info = client.tables.create( - "new_Product", - {"new_Price": "decimal", "new_InStock": "bool"}, - solution="MySolution", - ) - - # List tables - tables = client.tables.list() - - # Get table info - info = client.tables.get("new_Product") - - # Add columns - client.tables.add_columns("new_Product", {"new_Rating": "int"}) - - # Remove columns - client.tables.remove_columns("new_Product", "new_Rating") - - # Delete a table - client.tables.delete("new_Product") - - - .. py:method:: create(table: str, columns: Dict[str, Any], *, solution: Optional[str] = None, primary_column: Optional[str] = None, display_name: Optional[str] = None) -> PowerPlatform.Dataverse.models.table_info.TableInfo - - Create a custom table with the specified columns. - - :param table: Schema name of the table with customization prefix - (e.g. ``"new_MyTestTable"``). - :type table: :class:`str` - :param columns: Mapping of column schema names (with customization - prefix) to their types. Supported types include ``"string"`` - (or ``"text"``), ``"memo"`` (or ``"multiline"``), - ``"int"`` (or ``"integer"``), ``"decimal"`` - (or ``"money"``), ``"float"`` (or ``"double"``), ``"datetime"`` - (or ``"date"``), ``"bool"`` (or ``"boolean"``), ``"file"``, and - ``Enum`` subclasses - (for local option sets). - :type columns: :class:`dict` - :param solution: Optional solution unique name that should own the new - table. When omitted the table is created in the default solution. - :type solution: :class:`str` or None - :param primary_column: Optional primary name column schema name with - customization prefix (e.g. ``"new_ProductName"``). If not provided, - defaults to ``"{prefix}_Name"``. - :type primary_column: :class:`str` or None - :param display_name: Human-readable display name for the table - (e.g. ``"Product"``). When omitted, defaults to the table schema name. - :type display_name: :class:`str` or None - - :return: Table metadata with ``schema_name``, ``entity_set_name``, - ``logical_name``, ``metadata_id``, and ``columns_created``. - Supports dict-like access with legacy keys for backward - compatibility. - :rtype: :class:`~PowerPlatform.Dataverse.models.table_info.TableInfo` - - :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: - If table creation fails or the table already exists. - - .. rubric:: Example - - Create a table with simple columns:: - - from enum import IntEnum - - class ItemStatus(IntEnum): - ACTIVE = 1 - INACTIVE = 2 - - result = client.tables.create( - "new_Product", - { - "new_Title": "string", - "new_Price": "decimal", - "new_Status": ItemStatus, - }, - solution="MySolution", - primary_column="new_ProductName", - display_name="Product", - ) - print(f"Created: {result['table_schema_name']}") - - - - .. py:method:: delete(table: str) -> None - - Delete a custom table by schema name. - - :param table: Schema name of the table (e.g. ``"new_MyTestTable"``). - :type table: :class:`str` - - :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: - If the table does not exist or deletion fails. - - .. warning:: - This operation is irreversible and will delete all records in the - table along with the table definition. - - Example:: - - client.tables.delete("new_MyTestTable") - - - - .. py:method:: get(table: str) -> Optional[PowerPlatform.Dataverse.models.table_info.TableInfo] - - Get basic metadata for a table if it exists. - - :param table: Schema name of the table (e.g. ``"new_MyTestTable"`` - or ``"account"``). - :type table: :class:`str` - - :return: Table metadata, or ``None`` if the table is not found. - Supports dict-like access with legacy keys for backward - compatibility. - :rtype: :class:`~PowerPlatform.Dataverse.models.table_info.TableInfo` - or None - - Example:: - - info = client.tables.get("new_MyTestTable") - if info: - print(f"Logical name: {info['table_logical_name']}") - print(f"Entity set: {info['entity_set_name']}") - - - - .. py:method:: list(*, filter: Optional[str] = None, select: Optional[List[str]] = None) -> List[Dict[str, Any]] - - List all non-private tables in the Dataverse environment. - - By default returns every table where ``IsPrivate eq false``. Supply - an optional OData ``$filter`` expression to further narrow the results. - The expression is combined with the default ``IsPrivate eq false`` - clause using ``and``. - - :param filter: Optional OData ``$filter`` expression to further narrow - the list of returned tables (e.g. - ``"SchemaName eq 'Account'"``). Column names in filter - expressions must use the exact property names from the - ``EntityDefinitions`` metadata (typically PascalCase). - :type filter: :class:`str` or None - :param select: Optional list of property names to include in the - response (projected via the OData ``$select`` query option). - Property names must use the exact PascalCase names from the - ``EntityDefinitions`` metadata (e.g. - ``["LogicalName", "SchemaName", "DisplayName"]``). - When ``None`` (the default) or an empty list, all properties are - returned. - :type select: list[str] or None - - :return: List of EntityDefinition metadata dictionaries. - :rtype: list[dict] - - Example:: - - # List all non-private tables - tables = client.tables.list() - for table in tables: - print(table["LogicalName"]) - - # List only tables whose schema name starts with "new_" - custom_tables = client.tables.list( - filter="startswith(SchemaName, 'new_')" - ) - - # List tables with only specific properties - tables = client.tables.list( - select=["LogicalName", "SchemaName", "EntitySetName"] - ) - - - - .. py:method:: add_columns(table: str, columns: Dict[str, Any]) -> List[str] - - Add one or more columns to an existing table. - - :param table: Schema name of the table (e.g. ``"new_MyTestTable"``). - :type table: :class:`str` - :param columns: Mapping of column schema names (with customization - prefix) to their types. Supported types are the same as for - :meth:`create`. - :type columns: :class:`dict` - - :return: Schema names of the columns that were created. - :rtype: list[str] - - :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: - If the table does not exist. - - Example:: - - created = client.tables.add_columns( - "new_MyTestTable", - {"new_Notes": "string", "new_Active": "bool"}, - ) - print(created) # ['new_Notes', 'new_Active'] - - - - .. py:method:: remove_columns(table: str, columns: Union[str, List[str]]) -> List[str] - - Remove one or more columns from a table. - - :param table: Schema name of the table (e.g. ``"new_MyTestTable"``). - :type table: :class:`str` - :param columns: Column schema name or list of column schema names to - remove. Must include the customization prefix (e.g. - ``"new_TestColumn"``). - :type columns: str or list[str] - - :return: Schema names of the columns that were removed. - :rtype: list[str] - - :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: - If the table or a specified column does not exist. - - Example:: - - removed = client.tables.remove_columns( - "new_MyTestTable", - ["new_Notes", "new_Active"], - ) - print(removed) # ['new_Notes', 'new_Active'] - - - - .. py:method:: create_one_to_many_relationship(lookup: PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata, relationship: PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata, *, solution: Optional[str] = None) -> PowerPlatform.Dataverse.models.relationship.RelationshipInfo - - Create a one-to-many relationship between tables. - - This operation creates both the relationship and the lookup attribute - on the referencing table. - - :param lookup: Metadata defining the lookup attribute. - :type lookup: ~PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata - :param relationship: Metadata defining the relationship. - :type relationship: ~PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata - :param solution: Optional solution unique name to add relationship to. - :type solution: :class:`str` or None - - :return: Relationship metadata with ``relationship_id``, - ``relationship_schema_name``, ``relationship_type``, - ``lookup_schema_name``, ``referenced_entity``, and - ``referencing_entity``. - :rtype: :class:`~PowerPlatform.Dataverse.models.relationship.RelationshipInfo` - - :raises ~PowerPlatform.Dataverse.core.errors.HttpError: - If the Web API request fails. - - .. rubric:: Example - - Create a one-to-many relationship: Department (1) -> Employee (N):: - - from PowerPlatform.Dataverse.models import ( - LookupAttributeMetadata, - OneToManyRelationshipMetadata, - Label, - LocalizedLabel, - CascadeConfiguration, - ) - from PowerPlatform.Dataverse.common.constants import ( - CASCADE_BEHAVIOR_REMOVE_LINK, - ) - - lookup = LookupAttributeMetadata( - schema_name="new_DepartmentId", - display_name=Label( - localized_labels=[ - LocalizedLabel(label="Department", language_code=1033) - ] - ), - ) - - relationship = OneToManyRelationshipMetadata( - schema_name="new_Department_Employee", - referenced_entity="new_department", - referencing_entity="new_employee", - referenced_attribute="new_departmentid", - cascade_configuration=CascadeConfiguration( - delete=CASCADE_BEHAVIOR_REMOVE_LINK, - ), - ) - - result = client.tables.create_one_to_many_relationship(lookup, relationship) - print(f"Created lookup field: {result.lookup_schema_name}") - - - - .. py:method:: create_many_to_many_relationship(relationship: PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata, *, solution: Optional[str] = None) -> PowerPlatform.Dataverse.models.relationship.RelationshipInfo - - Create a many-to-many relationship between tables. - - This operation creates a many-to-many relationship and an intersect - table to manage the relationship. - - :param relationship: Metadata defining the many-to-many relationship. - :type relationship: ~PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata - :param solution: Optional solution unique name to add relationship to. - :type solution: :class:`str` or None - - :return: Relationship metadata with ``relationship_id``, - ``relationship_schema_name``, ``relationship_type``, - ``entity1_logical_name``, and ``entity2_logical_name``. - :rtype: :class:`~PowerPlatform.Dataverse.models.relationship.RelationshipInfo` - - :raises ~PowerPlatform.Dataverse.core.errors.HttpError: - If the Web API request fails. - - .. rubric:: Example - - Create a many-to-many relationship: Employee <-> Project:: - - from PowerPlatform.Dataverse.models import ( - ManyToManyRelationshipMetadata, - ) - - relationship = ManyToManyRelationshipMetadata( - schema_name="new_employee_project", - entity1_logical_name="new_employee", - entity2_logical_name="new_project", - ) - - result = client.tables.create_many_to_many_relationship(relationship) - print(f"Created: {result.relationship_schema_name}") - - - - .. py:method:: delete_relationship(relationship_id: str) -> None - - Delete a relationship by its metadata ID. - - :param relationship_id: The GUID of the relationship metadata. - :type relationship_id: :class:`str` - - :raises ~PowerPlatform.Dataverse.core.errors.HttpError: - If the Web API request fails. - - .. warning:: - Deleting a relationship also removes the associated lookup attribute - for one-to-many relationships. This operation is irreversible. - - Example:: - - client.tables.delete_relationship( - "12345678-1234-1234-1234-123456789abc" - ) - - - - .. py:method:: get_relationship(schema_name: str) -> Optional[PowerPlatform.Dataverse.models.relationship.RelationshipInfo] - - Retrieve relationship metadata by schema name. - - :param schema_name: The schema name of the relationship. - :type schema_name: :class:`str` - - :return: Relationship metadata, or ``None`` if not found. - :rtype: :class:`~PowerPlatform.Dataverse.models.relationship.RelationshipInfo` - or None - - :raises ~PowerPlatform.Dataverse.core.errors.HttpError: - If the Web API request fails. - - Example:: - - rel = client.tables.get_relationship("new_Department_Employee") - if rel: - print(f"Found: {rel.relationship_schema_name}") - - - - .. py:method:: create_lookup_field(referencing_table: str, lookup_field_name: str, referenced_table: str, *, display_name: Optional[str] = None, description: Optional[str] = None, required: bool = False, cascade_delete: str = CASCADE_BEHAVIOR_REMOVE_LINK, solution: Optional[str] = None, language_code: int = 1033) -> PowerPlatform.Dataverse.models.relationship.RelationshipInfo - - Create a simple lookup field relationship. - - This is a convenience method that wraps :meth:`create_one_to_many_relationship` - for the common case of adding a lookup field to an existing table. - - :param referencing_table: Logical name of the table that will have - the lookup field (child table). - :type referencing_table: :class:`str` - :param lookup_field_name: Schema name for the lookup field - (e.g., ``"new_AccountId"``). - :type lookup_field_name: :class:`str` - :param referenced_table: Logical name of the table being referenced - (parent table). - :type referenced_table: :class:`str` - :param display_name: Display name for the lookup field. Defaults to - the referenced table name. - :type display_name: :class:`str` or None - :param description: Optional description for the lookup field. - :type description: :class:`str` or None - :param required: Whether the lookup is required. Defaults to ``False``. - :type required: :class:`bool` - :param cascade_delete: Delete behavior (``"RemoveLink"``, - ``"Cascade"``, ``"Restrict"``). Defaults to ``"RemoveLink"``. - :type cascade_delete: :class:`str` - :param solution: Optional solution unique name to add the relationship - to. - :type solution: :class:`str` or None - :param language_code: Language code for labels. Defaults to 1033 - (English). - :type language_code: :class:`int` - - :return: Relationship metadata with ``relationship_id``, - ``relationship_schema_name``, ``relationship_type``, - ``lookup_schema_name``, ``referenced_entity``, and - ``referencing_entity``. - :rtype: :class:`~PowerPlatform.Dataverse.models.relationship.RelationshipInfo` - - :raises ~PowerPlatform.Dataverse.core.errors.HttpError: - If the Web API request fails. - - .. rubric:: Example - - Create a simple lookup field:: - - result = client.tables.create_lookup_field( - referencing_table="new_order", - lookup_field_name="new_AccountId", - referenced_table="account", - display_name="Account", - required=True, - cascade_delete=CASCADE_BEHAVIOR_REMOVE_LINK, - ) - print(f"Created lookup: {result['lookup_schema_name']}") - - - - .. py:method:: create_alternate_key(table: str, key_name: str, columns: List[str], *, display_name: Optional[str] = None, language_code: int = 1033) -> PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo - - Create an alternate key on a table. - - Alternate keys allow upsert operations to identify records by one or - more columns instead of the primary GUID. After creation the key is - queued for index building; its :attr:`~PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo.status` will - transition from ``"Pending"`` to ``"Active"`` once the index is ready. - - :param table: Schema name of the table (e.g. ``"new_Product"``). - :type table: :class:`str` - :param key_name: Schema name for the new alternate key - (e.g. ``"new_product_code_key"``). - :type key_name: :class:`str` - :param columns: List of column logical names that compose the key - (e.g. ``["new_productcode"]``). - :type columns: list[str] - :param display_name: Display name for the key. Defaults to - ``key_name`` if not provided. - :type display_name: :class:`str` or None - :param language_code: Language code for labels. Defaults to 1033 - (English). - :type language_code: :class:`int` - - :return: Metadata for the newly created alternate key. - :rtype: :class:`~PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo` - - :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: - If the table does not exist. - :raises ~PowerPlatform.Dataverse.core.errors.HttpError: - If the Web API request fails. - - .. rubric:: Example - - Create a single-column alternate key for upsert:: - - key = client.tables.create_alternate_key( - "new_Product", - "new_product_code_key", - ["new_productcode"], - display_name="Product Code", - ) - print(f"Key ID: {key.metadata_id}") - print(f"Columns: {key.key_attributes}") - - - - .. py:method:: get_alternate_keys(table: str) -> List[PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo] - - List all alternate keys defined on a table. - - :param table: Schema name of the table (e.g. ``"new_Product"``). - :type table: :class:`str` - - :return: List of alternate key metadata objects. May be empty if no - alternate keys are defined. - :rtype: list[~PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo] - - :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: - If the table does not exist. - :raises ~PowerPlatform.Dataverse.core.errors.HttpError: - If the Web API request fails. - - .. rubric:: Example - - List alternate keys and print their status:: - - keys = client.tables.get_alternate_keys("new_Product") - for key in keys: - print(f"{key.schema_name}: {key.status}") - - - - .. py:method:: delete_alternate_key(table: str, key_id: str) -> None - - Delete an alternate key by its metadata ID. - - :param table: Schema name of the table (e.g. ``"new_Product"``). - :type table: :class:`str` - :param key_id: Metadata GUID of the alternate key to delete. - :type key_id: :class:`str` - - :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: - If the table does not exist. - :raises ~PowerPlatform.Dataverse.core.errors.HttpError: - If the Web API request fails. - - .. warning:: - Deleting an alternate key that is in use by upsert operations will - cause those operations to fail. This operation is irreversible. - - Example:: - - client.tables.delete_alternate_key( - "new_Product", - "12345678-1234-1234-1234-123456789abc", - ) - - - - .. py:method:: list_columns(table: str, *, select: Optional[List[str]] = None, filter: Optional[str] = None) -> List[Dict[str, Any]] - - List all attribute (column) definitions for a table. - - :param table: Schema name of the table (e.g. ``"account"`` or - ``"new_Product"``). - :type table: :class:`str` - :param select: Optional list of property names to project via - ``$select``. Values are passed as-is (PascalCase). - :type select: list[str] or None - :param filter: Optional OData ``$filter`` expression. For example, - ``"AttributeType eq 'String'"`` returns only string columns. - :type filter: :class:`str` or None - - :return: List of raw attribute metadata dictionaries. - :rtype: list[dict[str, typing.Any]] - - :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: - If the table is not found. - :raises ~PowerPlatform.Dataverse.core.errors.HttpError: - If the Web API request fails. - - Example:: - - # List all columns on the account table - columns = client.tables.list_columns("account") - for col in columns: - print(f"{col['LogicalName']} ({col.get('AttributeType')})") - - # List only specific properties - columns = client.tables.list_columns( - "account", - select=["LogicalName", "SchemaName", "AttributeType"], - ) - - # Filter to only string attributes - columns = client.tables.list_columns( - "account", - filter="AttributeType eq 'String'", - ) - - - - .. py:method:: list_relationships(*, filter: Optional[str] = None, select: Optional[List[str]] = None) -> List[Dict[str, Any]] - - List all relationship definitions in the environment. - - :param filter: Optional OData ``$filter`` expression. For example, - ``"RelationshipType eq Microsoft.Dynamics.CRM.RelationshipType'OneToManyRelationship'"`` - returns only one-to-many relationships. - :type filter: :class:`str` or None - :param select: Optional list of property names to project via - ``$select``. Values are passed as-is (PascalCase). - :type select: list[str] or None - - :return: List of raw relationship metadata dictionaries. - :rtype: list[dict[str, typing.Any]] - - :raises ~PowerPlatform.Dataverse.core.errors.HttpError: - If the Web API request fails. - - Example:: - - # List all relationships - rels = client.tables.list_relationships() - for rel in rels: - print(f"{rel['SchemaName']} ({rel.get('@odata.type')})") - - # Filter by type - one_to_many = client.tables.list_relationships( - filter="RelationshipType eq Microsoft.Dynamics.CRM.RelationshipType'OneToManyRelationship'" - ) - - # Select specific properties - rels = client.tables.list_relationships( - select=["SchemaName", "ReferencedEntity", "ReferencingEntity"] - ) - - - - .. py:method:: list_table_relationships(table: str, *, filter: Optional[str] = None, select: Optional[List[str]] = None) -> List[Dict[str, Any]] - - List all relationships for a specific table. - - Combines one-to-many, many-to-one, and many-to-many relationships - for the given table by querying - ``EntityDefinitions({id})/OneToManyRelationships``, - ``EntityDefinitions({id})/ManyToOneRelationships``, and - ``EntityDefinitions({id})/ManyToManyRelationships``. - - :param table: Schema name of the table (e.g. ``"account"``). - :type table: :class:`str` - :param filter: Optional OData ``$filter`` expression applied to each - sub-request. - :type filter: :class:`str` or None - :param select: Optional list of property names to project via - ``$select``. Values are passed as-is (PascalCase). - :type select: list[str] or None - - :return: Combined list of one-to-many, many-to-one, and many-to-many - relationship metadata dictionaries. - :rtype: list[dict[str, typing.Any]] - - :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: - If the table is not found. - :raises ~PowerPlatform.Dataverse.core.errors.HttpError: - If the Web API request fails. - - Example:: - - # List all relationships for the account table - rels = client.tables.list_table_relationships("account") - for rel in rels: - print(f"{rel['SchemaName']} -> {rel.get('@odata.type')}") - - - diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/utils/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/utils/index.rst.txt deleted file mode 100644 index bf579628..00000000 --- a/docs_local/_build/_sources/autoapi/PowerPlatform/Dataverse/utils/index.rst.txt +++ /dev/null @@ -1,13 +0,0 @@ -PowerPlatform.Dataverse.utils -============================= - -.. py:module:: PowerPlatform.Dataverse.utils - -.. autoapi-nested-parse:: - - Utilities and adapters for the Dataverse SDK. - - Placeholder module for future utility adapters. - - - diff --git a/docs_local/_build/_sources/autoapi/PowerPlatform/index.rst.txt b/docs_local/_build/_sources/autoapi/PowerPlatform/index.rst.txt deleted file mode 100644 index 6bf6b5a5..00000000 --- a/docs_local/_build/_sources/autoapi/PowerPlatform/index.rst.txt +++ /dev/null @@ -1,15 +0,0 @@ -PowerPlatform -============= - -.. py:module:: PowerPlatform - - -Submodules ----------- - -.. toctree:: - :maxdepth: 1 - - /autoapi/PowerPlatform/Dataverse/index - - diff --git a/docs_local/_build/_sources/autoapi/index.rst.txt b/docs_local/_build/_sources/autoapi/index.rst.txt deleted file mode 100644 index 79277872..00000000 --- a/docs_local/_build/_sources/autoapi/index.rst.txt +++ /dev/null @@ -1,11 +0,0 @@ -API Reference -============= - -This page contains auto-generated API reference documentation [#f1]_. - -.. toctree:: - :titlesonly: - - /autoapi/PowerPlatform/index - -.. [#f1] Created with `sphinx-autoapi `_ \ No newline at end of file diff --git a/docs_local/_build/_sources/index.rst.txt b/docs_local/_build/_sources/index.rst.txt deleted file mode 100644 index a3445f40..00000000 --- a/docs_local/_build/_sources/index.rst.txt +++ /dev/null @@ -1,8 +0,0 @@ -PowerPlatform Dataverse Client SDK -=================================== - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - - autoapi/index diff --git a/docs_local/_build/_static/alabaster.css b/docs_local/_build/_static/alabaster.css deleted file mode 100644 index e3174bf9..00000000 --- a/docs_local/_build/_static/alabaster.css +++ /dev/null @@ -1,708 +0,0 @@ -@import url("basic.css"); - -/* -- page layout ----------------------------------------------------------- */ - -body { - font-family: Georgia, serif; - font-size: 17px; - background-color: #fff; - color: #000; - margin: 0; - padding: 0; -} - - -div.document { - width: 940px; - margin: 30px auto 0 auto; -} - -div.documentwrapper { - float: left; - width: 100%; -} - -div.bodywrapper { - margin: 0 0 0 220px; -} - -div.sphinxsidebar { - width: 220px; - font-size: 14px; - line-height: 1.5; -} - -hr { - border: 1px solid #B1B4B6; -} - -div.body { - background-color: #fff; - color: #3E4349; - padding: 0 30px 0 30px; -} - -div.body > .section { - text-align: left; -} - -div.footer { - width: 940px; - margin: 20px auto 30px auto; - font-size: 14px; - color: #888; - text-align: right; -} - -div.footer a { - color: #888; -} - -p.caption { - font-family: inherit; - font-size: inherit; -} - - -div.relations { - display: none; -} - - -div.sphinxsidebar { - max-height: 100%; - overflow-y: auto; -} - -div.sphinxsidebar a { - color: #444; - text-decoration: none; - border-bottom: 1px dotted #999; -} - -div.sphinxsidebar a:hover { - border-bottom: 1px solid #999; -} - -div.sphinxsidebarwrapper { - padding: 18px 10px; -} - -div.sphinxsidebarwrapper p.logo { - padding: 0; - margin: -10px 0 0 0px; - text-align: center; -} - -div.sphinxsidebarwrapper h1.logo { - margin-top: -10px; - text-align: center; - margin-bottom: 5px; - text-align: left; -} - -div.sphinxsidebarwrapper h1.logo-name { - margin-top: 0px; -} - -div.sphinxsidebarwrapper p.blurb { - margin-top: 0; - font-style: normal; -} - -div.sphinxsidebar h3, -div.sphinxsidebar h4 { - font-family: Georgia, serif; - color: #444; - font-size: 24px; - font-weight: normal; - margin: 0 0 5px 0; - padding: 0; -} - -div.sphinxsidebar h4 { - font-size: 20px; -} - -div.sphinxsidebar h3 a { - color: #444; -} - -div.sphinxsidebar p.logo a, -div.sphinxsidebar h3 a, -div.sphinxsidebar p.logo a:hover, -div.sphinxsidebar h3 a:hover { - border: none; -} - -div.sphinxsidebar p { - color: #555; - margin: 10px 0; -} - -div.sphinxsidebar ul { - margin: 10px 0; - padding: 0; - color: #000; -} - -div.sphinxsidebar ul li.toctree-l1 > a { - font-size: 120%; -} - -div.sphinxsidebar ul li.toctree-l2 > a { - font-size: 110%; -} - -div.sphinxsidebar input { - border: 1px solid #CCC; - font-family: Georgia, serif; - font-size: 1em; -} - -div.sphinxsidebar #searchbox input[type="text"] { - width: 160px; -} - -div.sphinxsidebar .search > div { - display: table-cell; -} - -div.sphinxsidebar hr { - border: none; - height: 1px; - color: #AAA; - background: #AAA; - - text-align: left; - margin-left: 0; - width: 50%; -} - -div.sphinxsidebar .badge { - border-bottom: none; -} - -div.sphinxsidebar .badge:hover { - border-bottom: none; -} - -/* To address an issue with donation coming after search */ -div.sphinxsidebar h3.donation { - margin-top: 10px; -} - -/* -- body styles ----------------------------------------------------------- */ - -a { - color: #004B6B; - text-decoration: underline; -} - -a:hover { - color: #6D4100; - text-decoration: underline; -} - -div.body h1, -div.body h2, -div.body h3, -div.body h4, -div.body h5, -div.body h6 { - font-family: Georgia, serif; - font-weight: normal; - margin: 30px 0px 10px 0px; - padding: 0; -} - -div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; } -div.body h2 { font-size: 180%; } -div.body h3 { font-size: 150%; } -div.body h4 { font-size: 130%; } -div.body h5 { font-size: 100%; } -div.body h6 { font-size: 100%; } - -a.headerlink { - color: #DDD; - padding: 0 4px; - text-decoration: none; -} - -a.headerlink:hover { - color: #444; - background: #EAEAEA; -} - -div.body p, div.body dd, div.body li { - line-height: 1.4em; -} - -div.admonition { - margin: 20px 0px; - padding: 10px 30px; - background-color: #EEE; - border: 1px solid #CCC; -} - -div.admonition tt.xref, div.admonition code.xref, div.admonition a tt { - background-color: #FBFBFB; - border-bottom: 1px solid #fafafa; -} - -div.admonition p.admonition-title { - font-family: Georgia, serif; - font-weight: normal; - font-size: 24px; - margin: 0 0 10px 0; - padding: 0; - line-height: 1; -} - -div.admonition p.last { - margin-bottom: 0; -} - -div.highlight { - background-color: #fff; -} - -dt:target, .highlight { - background: #FAF3E8; -} - -div.warning { - background-color: #FCC; - border: 1px solid #FAA; -} - -div.danger { - background-color: #FCC; - border: 1px solid #FAA; - -moz-box-shadow: 2px 2px 4px #D52C2C; - -webkit-box-shadow: 2px 2px 4px #D52C2C; - box-shadow: 2px 2px 4px #D52C2C; -} - -div.error { - background-color: #FCC; - border: 1px solid #FAA; - -moz-box-shadow: 2px 2px 4px #D52C2C; - -webkit-box-shadow: 2px 2px 4px #D52C2C; - box-shadow: 2px 2px 4px #D52C2C; -} - -div.caution { - background-color: #FCC; - border: 1px solid #FAA; -} - -div.attention { - background-color: #FCC; - border: 1px solid #FAA; -} - -div.important { - background-color: #EEE; - border: 1px solid #CCC; -} - -div.note { - background-color: #EEE; - border: 1px solid #CCC; -} - -div.tip { - background-color: #EEE; - border: 1px solid #CCC; -} - -div.hint { - background-color: #EEE; - border: 1px solid #CCC; -} - -div.seealso { - background-color: #EEE; - border: 1px solid #CCC; -} - -div.topic { - background-color: #EEE; -} - -p.admonition-title { - display: inline; -} - -p.admonition-title:after { - content: ":"; -} - -pre, tt, code { - font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; - font-size: 0.9em; -} - -.hll { - background-color: #FFC; - margin: 0 -12px; - padding: 0 12px; - display: block; -} - -img.screenshot { -} - -tt.descname, tt.descclassname, code.descname, code.descclassname { - font-size: 0.95em; -} - -tt.descname, code.descname { - padding-right: 0.08em; -} - -img.screenshot { - -moz-box-shadow: 2px 2px 4px #EEE; - -webkit-box-shadow: 2px 2px 4px #EEE; - box-shadow: 2px 2px 4px #EEE; -} - -table.docutils { - border: 1px solid #888; - -moz-box-shadow: 2px 2px 4px #EEE; - -webkit-box-shadow: 2px 2px 4px #EEE; - box-shadow: 2px 2px 4px #EEE; -} - -table.docutils td, table.docutils th { - border: 1px solid #888; - padding: 0.25em 0.7em; -} - -table.field-list, table.footnote { - border: none; - -moz-box-shadow: none; - -webkit-box-shadow: none; - box-shadow: none; -} - -table.footnote { - margin: 15px 0; - width: 100%; - border: 1px solid #EEE; - background: #FDFDFD; - font-size: 0.9em; -} - -table.footnote + table.footnote { - margin-top: -15px; - border-top: none; -} - -table.field-list th { - padding: 0 0.8em 0 0; -} - -table.field-list td { - padding: 0; -} - -table.field-list p { - margin-bottom: 0.8em; -} - -/* Cloned from - * https://github.com/sphinx-doc/sphinx/commit/ef60dbfce09286b20b7385333d63a60321784e68 - */ -.field-name { - -moz-hyphens: manual; - -ms-hyphens: manual; - -webkit-hyphens: manual; - hyphens: manual; -} - -table.footnote td.label { - width: .1px; - padding: 0.3em 0 0.3em 0.5em; -} - -table.footnote td { - padding: 0.3em 0.5em; -} - -dl { - margin-left: 0; - margin-right: 0; - margin-top: 0; - padding: 0; -} - -dl dd { - margin-left: 30px; -} - -blockquote { - margin: 0 0 0 30px; - padding: 0; -} - -ul, ol { - /* Matches the 30px from the narrow-screen "li > ul" selector below */ - margin: 10px 0 10px 30px; - padding: 0; -} - -pre { - background: #EEE; - padding: 7px 30px; - margin: 15px 0px; - line-height: 1.3em; -} - -div.viewcode-block:target { - background: #ffd; -} - -dl pre, blockquote pre, li pre { - margin-left: 0; - padding-left: 30px; -} - -tt, code { - background-color: #ecf0f3; - color: #222; - /* padding: 1px 2px; */ -} - -tt.xref, code.xref, a tt { - background-color: #FBFBFB; - border-bottom: 1px solid #fff; -} - -a.reference { - text-decoration: none; - border-bottom: 1px dotted #004B6B; -} - -/* Don't put an underline on images */ -a.image-reference, a.image-reference:hover { - border-bottom: none; -} - -a.reference:hover { - border-bottom: 1px solid #6D4100; -} - -a.footnote-reference { - text-decoration: none; - font-size: 0.7em; - vertical-align: top; - border-bottom: 1px dotted #004B6B; -} - -a.footnote-reference:hover { - border-bottom: 1px solid #6D4100; -} - -a:hover tt, a:hover code { - background: #EEE; -} - - -@media screen and (max-width: 870px) { - - div.sphinxsidebar { - display: none; - } - - div.document { - width: 100%; - - } - - div.documentwrapper { - margin-left: 0; - margin-top: 0; - margin-right: 0; - margin-bottom: 0; - } - - div.bodywrapper { - margin-top: 0; - margin-right: 0; - margin-bottom: 0; - margin-left: 0; - } - - ul { - margin-left: 0; - } - - li > ul { - /* Matches the 30px from the "ul, ol" selector above */ - margin-left: 30px; - } - - .document { - width: auto; - } - - .footer { - width: auto; - } - - .bodywrapper { - margin: 0; - } - - .footer { - width: auto; - } - - .github { - display: none; - } - - - -} - - - -@media screen and (max-width: 875px) { - - body { - margin: 0; - padding: 20px 30px; - } - - div.documentwrapper { - float: none; - background: #fff; - } - - div.sphinxsidebar { - display: block; - float: none; - width: 102.5%; - margin: 50px -30px -20px -30px; - padding: 10px 20px; - background: #333; - color: #FFF; - } - - div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p, - div.sphinxsidebar h3 a { - color: #fff; - } - - div.sphinxsidebar a { - color: #AAA; - } - - div.sphinxsidebar p.logo { - display: none; - } - - div.document { - width: 100%; - margin: 0; - } - - div.footer { - display: none; - } - - div.bodywrapper { - margin: 0; - } - - div.body { - min-height: 0; - padding: 0; - } - - .rtd_doc_footer { - display: none; - } - - .document { - width: auto; - } - - .footer { - width: auto; - } - - .footer { - width: auto; - } - - .github { - display: none; - } -} - - -/* misc. */ - -.revsys-inline { - display: none!important; -} - -/* Hide ugly table cell borders in ..bibliography:: directive output */ -table.docutils.citation, table.docutils.citation td, table.docutils.citation th { - border: none; - /* Below needed in some edge cases; if not applied, bottom shadows appear */ - -moz-box-shadow: none; - -webkit-box-shadow: none; - box-shadow: none; -} - - -/* relbar */ - -.related { - line-height: 30px; - width: 100%; - font-size: 0.9rem; -} - -.related.top { - border-bottom: 1px solid #EEE; - margin-bottom: 20px; -} - -.related.bottom { - border-top: 1px solid #EEE; -} - -.related ul { - padding: 0; - margin: 0; - list-style: none; -} - -.related li { - display: inline; -} - -nav#rellinks { - float: right; -} - -nav#rellinks li+li:before { - content: "|"; -} - -nav#breadcrumbs li+li:before { - content: "\00BB"; -} - -/* Hide certain items when printing */ -@media print { - div.related { - display: none; - } -} \ No newline at end of file diff --git a/docs_local/_build/_static/base-stemmer.js b/docs_local/_build/_static/base-stemmer.js deleted file mode 100644 index e6fa0c49..00000000 --- a/docs_local/_build/_static/base-stemmer.js +++ /dev/null @@ -1,476 +0,0 @@ -// @ts-check - -/**@constructor*/ -BaseStemmer = function() { - /** @protected */ - this.current = ''; - this.cursor = 0; - this.limit = 0; - this.limit_backward = 0; - this.bra = 0; - this.ket = 0; - - /** - * @param {string} value - */ - this.setCurrent = function(value) { - this.current = value; - this.cursor = 0; - this.limit = this.current.length; - this.limit_backward = 0; - this.bra = this.cursor; - this.ket = this.limit; - }; - - /** - * @return {string} - */ - this.getCurrent = function() { - return this.current; - }; - - /** - * @param {BaseStemmer} other - */ - this.copy_from = function(other) { - /** @protected */ - this.current = other.current; - this.cursor = other.cursor; - this.limit = other.limit; - this.limit_backward = other.limit_backward; - this.bra = other.bra; - this.ket = other.ket; - }; - - /** - * @param {number[]} s - * @param {number} min - * @param {number} max - * @return {boolean} - */ - this.in_grouping = function(s, min, max) { - /** @protected */ - if (this.cursor >= this.limit) return false; - var ch = this.current.charCodeAt(this.cursor); - if (ch > max || ch < min) return false; - ch -= min; - if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0) return false; - this.cursor++; - return true; - }; - - /** - * @param {number[]} s - * @param {number} min - * @param {number} max - * @return {boolean} - */ - this.go_in_grouping = function(s, min, max) { - /** @protected */ - while (this.cursor < this.limit) { - var ch = this.current.charCodeAt(this.cursor); - if (ch > max || ch < min) - return true; - ch -= min; - if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0) - return true; - this.cursor++; - } - return false; - }; - - /** - * @param {number[]} s - * @param {number} min - * @param {number} max - * @return {boolean} - */ - this.in_grouping_b = function(s, min, max) { - /** @protected */ - if (this.cursor <= this.limit_backward) return false; - var ch = this.current.charCodeAt(this.cursor - 1); - if (ch > max || ch < min) return false; - ch -= min; - if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0) return false; - this.cursor--; - return true; - }; - - /** - * @param {number[]} s - * @param {number} min - * @param {number} max - * @return {boolean} - */ - this.go_in_grouping_b = function(s, min, max) { - /** @protected */ - while (this.cursor > this.limit_backward) { - var ch = this.current.charCodeAt(this.cursor - 1); - if (ch > max || ch < min) return true; - ch -= min; - if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0) return true; - this.cursor--; - } - return false; - }; - - /** - * @param {number[]} s - * @param {number} min - * @param {number} max - * @return {boolean} - */ - this.out_grouping = function(s, min, max) { - /** @protected */ - if (this.cursor >= this.limit) return false; - var ch = this.current.charCodeAt(this.cursor); - if (ch > max || ch < min) { - this.cursor++; - return true; - } - ch -= min; - if ((s[ch >>> 3] & (0X1 << (ch & 0x7))) == 0) { - this.cursor++; - return true; - } - return false; - }; - - /** - * @param {number[]} s - * @param {number} min - * @param {number} max - * @return {boolean} - */ - this.go_out_grouping = function(s, min, max) { - /** @protected */ - while (this.cursor < this.limit) { - var ch = this.current.charCodeAt(this.cursor); - if (ch <= max && ch >= min) { - ch -= min; - if ((s[ch >>> 3] & (0X1 << (ch & 0x7))) != 0) { - return true; - } - } - this.cursor++; - } - return false; - }; - - /** - * @param {number[]} s - * @param {number} min - * @param {number} max - * @return {boolean} - */ - this.out_grouping_b = function(s, min, max) { - /** @protected */ - if (this.cursor <= this.limit_backward) return false; - var ch = this.current.charCodeAt(this.cursor - 1); - if (ch > max || ch < min) { - this.cursor--; - return true; - } - ch -= min; - if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0) { - this.cursor--; - return true; - } - return false; - }; - - /** - * @param {number[]} s - * @param {number} min - * @param {number} max - * @return {boolean} - */ - this.go_out_grouping_b = function(s, min, max) { - /** @protected */ - while (this.cursor > this.limit_backward) { - var ch = this.current.charCodeAt(this.cursor - 1); - if (ch <= max && ch >= min) { - ch -= min; - if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) != 0) { - return true; - } - } - this.cursor--; - } - return false; - }; - - /** - * @param {string} s - * @return {boolean} - */ - this.eq_s = function(s) - { - /** @protected */ - if (this.limit - this.cursor < s.length) return false; - if (this.current.slice(this.cursor, this.cursor + s.length) != s) - { - return false; - } - this.cursor += s.length; - return true; - }; - - /** - * @param {string} s - * @return {boolean} - */ - this.eq_s_b = function(s) - { - /** @protected */ - if (this.cursor - this.limit_backward < s.length) return false; - if (this.current.slice(this.cursor - s.length, this.cursor) != s) - { - return false; - } - this.cursor -= s.length; - return true; - }; - - /** - * @param {Among[]} v - * @return {number} - */ - this.find_among = function(v) - { - /** @protected */ - var i = 0; - var j = v.length; - - var c = this.cursor; - var l = this.limit; - - var common_i = 0; - var common_j = 0; - - var first_key_inspected = false; - - while (true) - { - var k = i + ((j - i) >>> 1); - var diff = 0; - var common = common_i < common_j ? common_i : common_j; // smaller - // w[0]: string, w[1]: substring_i, w[2]: result, w[3]: function (optional) - var w = v[k]; - var i2; - for (i2 = common; i2 < w[0].length; i2++) - { - if (c + common == l) - { - diff = -1; - break; - } - diff = this.current.charCodeAt(c + common) - w[0].charCodeAt(i2); - if (diff != 0) break; - common++; - } - if (diff < 0) - { - j = k; - common_j = common; - } - else - { - i = k; - common_i = common; - } - if (j - i <= 1) - { - if (i > 0) break; // v->s has been inspected - if (j == i) break; // only one item in v - - // - but now we need to go round once more to get - // v->s inspected. This looks messy, but is actually - // the optimal approach. - - if (first_key_inspected) break; - first_key_inspected = true; - } - } - do { - var w = v[i]; - if (common_i >= w[0].length) - { - this.cursor = c + w[0].length; - if (w.length < 4) return w[2]; - var res = w[3](this); - this.cursor = c + w[0].length; - if (res) return w[2]; - } - i = w[1]; - } while (i >= 0); - return 0; - }; - - // find_among_b is for backwards processing. Same comments apply - /** - * @param {Among[]} v - * @return {number} - */ - this.find_among_b = function(v) - { - /** @protected */ - var i = 0; - var j = v.length - - var c = this.cursor; - var lb = this.limit_backward; - - var common_i = 0; - var common_j = 0; - - var first_key_inspected = false; - - while (true) - { - var k = i + ((j - i) >> 1); - var diff = 0; - var common = common_i < common_j ? common_i : common_j; - var w = v[k]; - var i2; - for (i2 = w[0].length - 1 - common; i2 >= 0; i2--) - { - if (c - common == lb) - { - diff = -1; - break; - } - diff = this.current.charCodeAt(c - 1 - common) - w[0].charCodeAt(i2); - if (diff != 0) break; - common++; - } - if (diff < 0) - { - j = k; - common_j = common; - } - else - { - i = k; - common_i = common; - } - if (j - i <= 1) - { - if (i > 0) break; - if (j == i) break; - if (first_key_inspected) break; - first_key_inspected = true; - } - } - do { - var w = v[i]; - if (common_i >= w[0].length) - { - this.cursor = c - w[0].length; - if (w.length < 4) return w[2]; - var res = w[3](this); - this.cursor = c - w[0].length; - if (res) return w[2]; - } - i = w[1]; - } while (i >= 0); - return 0; - }; - - /* to replace chars between c_bra and c_ket in this.current by the - * chars in s. - */ - /** - * @param {number} c_bra - * @param {number} c_ket - * @param {string} s - * @return {number} - */ - this.replace_s = function(c_bra, c_ket, s) - { - /** @protected */ - var adjustment = s.length - (c_ket - c_bra); - this.current = this.current.slice(0, c_bra) + s + this.current.slice(c_ket); - this.limit += adjustment; - if (this.cursor >= c_ket) this.cursor += adjustment; - else if (this.cursor > c_bra) this.cursor = c_bra; - return adjustment; - }; - - /** - * @return {boolean} - */ - this.slice_check = function() - { - /** @protected */ - if (this.bra < 0 || - this.bra > this.ket || - this.ket > this.limit || - this.limit > this.current.length) - { - return false; - } - return true; - }; - - /** - * @param {number} c_bra - * @return {boolean} - */ - this.slice_from = function(s) - { - /** @protected */ - var result = false; - if (this.slice_check()) - { - this.replace_s(this.bra, this.ket, s); - result = true; - } - return result; - }; - - /** - * @return {boolean} - */ - this.slice_del = function() - { - /** @protected */ - return this.slice_from(""); - }; - - /** - * @param {number} c_bra - * @param {number} c_ket - * @param {string} s - */ - this.insert = function(c_bra, c_ket, s) - { - /** @protected */ - var adjustment = this.replace_s(c_bra, c_ket, s); - if (c_bra <= this.bra) this.bra += adjustment; - if (c_bra <= this.ket) this.ket += adjustment; - }; - - /** - * @return {string} - */ - this.slice_to = function() - { - /** @protected */ - var result = ''; - if (this.slice_check()) - { - result = this.current.slice(this.bra, this.ket); - } - return result; - }; - - /** - * @return {string} - */ - this.assign_to = function() - { - /** @protected */ - return this.current.slice(0, this.limit); - }; -}; diff --git a/docs_local/_build/_static/basic.css b/docs_local/_build/_static/basic.css deleted file mode 100644 index 0028826d..00000000 --- a/docs_local/_build/_static/basic.css +++ /dev/null @@ -1,906 +0,0 @@ -/* - * Sphinx stylesheet -- basic theme. - */ - -/* -- main layout ----------------------------------------------------------- */ - -div.clearer { - clear: both; -} - -div.section::after { - display: block; - content: ''; - clear: left; -} - -/* -- relbar ---------------------------------------------------------------- */ - -div.related { - width: 100%; - font-size: 90%; -} - -div.related h3 { - display: none; -} - -div.related ul { - margin: 0; - padding: 0 0 0 10px; - list-style: none; -} - -div.related li { - display: inline; -} - -div.related li.right { - float: right; - margin-right: 5px; -} - -/* -- sidebar --------------------------------------------------------------- */ - -div.sphinxsidebarwrapper { - padding: 10px 5px 0 10px; -} - -div.sphinxsidebar { - float: left; - width: 230px; - margin-left: -100%; - font-size: 90%; - word-wrap: break-word; - overflow-wrap : break-word; -} - -div.sphinxsidebar ul { - list-style: none; -} - -div.sphinxsidebar ul ul, -div.sphinxsidebar ul.want-points { - margin-left: 20px; - list-style: square; -} - -div.sphinxsidebar ul ul { - margin-top: 0; - margin-bottom: 0; -} - -div.sphinxsidebar form { - margin-top: 10px; -} - -div.sphinxsidebar input { - border: 1px solid #98dbcc; - font-family: sans-serif; - font-size: 1em; -} - -div.sphinxsidebar #searchbox form.search { - overflow: hidden; -} - -div.sphinxsidebar #searchbox input[type="text"] { - float: left; - width: 80%; - padding: 0.25em; - box-sizing: border-box; -} - -div.sphinxsidebar #searchbox input[type="submit"] { - float: left; - width: 20%; - border-left: none; - padding: 0.25em; - box-sizing: border-box; -} - - -img { - border: 0; - max-width: 100%; -} - -/* -- search page ----------------------------------------------------------- */ - -ul.search { - margin-top: 10px; -} - -ul.search li { - padding: 5px 0; -} - -ul.search li a { - font-weight: bold; -} - -ul.search li p.context { - color: #888; - margin: 2px 0 0 30px; - text-align: left; -} - -ul.keywordmatches li.goodmatch a { - font-weight: bold; -} - -/* -- index page ------------------------------------------------------------ */ - -table.contentstable { - width: 90%; - margin-left: auto; - margin-right: auto; -} - -table.contentstable p.biglink { - line-height: 150%; -} - -a.biglink { - font-size: 1.3em; -} - -span.linkdescr { - font-style: italic; - padding-top: 5px; - font-size: 90%; -} - -/* -- general index --------------------------------------------------------- */ - -table.indextable { - width: 100%; -} - -table.indextable td { - text-align: left; - vertical-align: top; -} - -table.indextable ul { - margin-top: 0; - margin-bottom: 0; - list-style-type: none; -} - -table.indextable > tbody > tr > td > ul { - padding-left: 0em; -} - -table.indextable tr.pcap { - height: 10px; -} - -table.indextable tr.cap { - margin-top: 10px; - background-color: #f2f2f2; -} - -img.toggler { - margin-right: 3px; - margin-top: 3px; - cursor: pointer; -} - -div.modindex-jumpbox { - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - margin: 1em 0 1em 0; - padding: 0.4em; -} - -div.genindex-jumpbox { - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - margin: 1em 0 1em 0; - padding: 0.4em; -} - -/* -- domain module index --------------------------------------------------- */ - -table.modindextable td { - padding: 2px; - border-collapse: collapse; -} - -/* -- general body styles --------------------------------------------------- */ - -div.body { - min-width: inherit; - max-width: 800px; -} - -div.body p, div.body dd, div.body li, div.body blockquote { - -moz-hyphens: auto; - -ms-hyphens: auto; - -webkit-hyphens: auto; - hyphens: auto; -} - -a.headerlink { - visibility: hidden; -} - -a:visited { - color: #551A8B; -} - -h1:hover > a.headerlink, -h2:hover > a.headerlink, -h3:hover > a.headerlink, -h4:hover > a.headerlink, -h5:hover > a.headerlink, -h6:hover > a.headerlink, -dt:hover > a.headerlink, -caption:hover > a.headerlink, -p.caption:hover > a.headerlink, -div.code-block-caption:hover > a.headerlink { - visibility: visible; -} - -div.body p.caption { - text-align: inherit; -} - -div.body td { - text-align: left; -} - -.first { - margin-top: 0 !important; -} - -p.rubric { - margin-top: 30px; - font-weight: bold; -} - -img.align-left, figure.align-left, .figure.align-left, object.align-left { - clear: left; - float: left; - margin-right: 1em; -} - -img.align-right, figure.align-right, .figure.align-right, object.align-right { - clear: right; - float: right; - margin-left: 1em; -} - -img.align-center, figure.align-center, .figure.align-center, object.align-center { - display: block; - margin-left: auto; - margin-right: auto; -} - -img.align-default, figure.align-default, .figure.align-default { - display: block; - margin-left: auto; - margin-right: auto; -} - -.align-left { - text-align: left; -} - -.align-center { - text-align: center; -} - -.align-default { - text-align: center; -} - -.align-right { - text-align: right; -} - -/* -- sidebars -------------------------------------------------------------- */ - -div.sidebar, -aside.sidebar { - margin: 0 0 0.5em 1em; - border: 1px solid #ddb; - padding: 7px; - background-color: #ffe; - width: 40%; - float: right; - clear: right; - overflow-x: auto; -} - -p.sidebar-title { - font-weight: bold; -} - -nav.contents, -aside.topic, -div.admonition, div.topic, blockquote { - clear: left; -} - -/* -- topics ---------------------------------------------------------------- */ - -nav.contents, -aside.topic, -div.topic { - border: 1px solid #ccc; - padding: 7px; - margin: 10px 0 10px 0; -} - -p.topic-title { - font-size: 1.1em; - font-weight: bold; - margin-top: 10px; -} - -/* -- admonitions ----------------------------------------------------------- */ - -div.admonition { - margin-top: 10px; - margin-bottom: 10px; - padding: 7px; -} - -div.admonition dt { - font-weight: bold; -} - -p.admonition-title { - margin: 0px 10px 5px 0px; - font-weight: bold; -} - -div.body p.centered { - text-align: center; - margin-top: 25px; -} - -/* -- content of sidebars/topics/admonitions -------------------------------- */ - -div.sidebar > :last-child, -aside.sidebar > :last-child, -nav.contents > :last-child, -aside.topic > :last-child, -div.topic > :last-child, -div.admonition > :last-child { - margin-bottom: 0; -} - -div.sidebar::after, -aside.sidebar::after, -nav.contents::after, -aside.topic::after, -div.topic::after, -div.admonition::after, -blockquote::after { - display: block; - content: ''; - clear: both; -} - -/* -- tables ---------------------------------------------------------------- */ - -table.docutils { - margin-top: 10px; - margin-bottom: 10px; - border: 0; - border-collapse: collapse; -} - -table.align-center { - margin-left: auto; - margin-right: auto; -} - -table.align-default { - margin-left: auto; - margin-right: auto; -} - -table caption span.caption-number { - font-style: italic; -} - -table caption span.caption-text { -} - -table.docutils td, table.docutils th { - padding: 1px 8px 1px 5px; - border-top: 0; - border-left: 0; - border-right: 0; - border-bottom: 1px solid #aaa; -} - -th { - text-align: left; - padding-right: 5px; -} - -table.citation { - border-left: solid 1px gray; - margin-left: 1px; -} - -table.citation td { - border-bottom: none; -} - -th > :first-child, -td > :first-child { - margin-top: 0px; -} - -th > :last-child, -td > :last-child { - margin-bottom: 0px; -} - -/* -- figures --------------------------------------------------------------- */ - -div.figure, figure { - margin: 0.5em; - padding: 0.5em; -} - -div.figure p.caption, figcaption { - padding: 0.3em; -} - -div.figure p.caption span.caption-number, -figcaption span.caption-number { - font-style: italic; -} - -div.figure p.caption span.caption-text, -figcaption span.caption-text { -} - -/* -- field list styles ----------------------------------------------------- */ - -table.field-list td, table.field-list th { - border: 0 !important; -} - -.field-list ul { - margin: 0; - padding-left: 1em; -} - -.field-list p { - margin: 0; -} - -.field-name { - -moz-hyphens: manual; - -ms-hyphens: manual; - -webkit-hyphens: manual; - hyphens: manual; -} - -/* -- hlist styles ---------------------------------------------------------- */ - -table.hlist { - margin: 1em 0; -} - -table.hlist td { - vertical-align: top; -} - -/* -- object description styles --------------------------------------------- */ - -.sig { - font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; -} - -.sig-name, code.descname { - background-color: transparent; - font-weight: bold; -} - -.sig-name { - font-size: 1.1em; -} - -code.descname { - font-size: 1.2em; -} - -.sig-prename, code.descclassname { - background-color: transparent; -} - -.optional { - font-size: 1.3em; -} - -.sig-paren { - font-size: larger; -} - -.sig-param.n { - font-style: italic; -} - -/* C++ specific styling */ - -.sig-inline.c-texpr, -.sig-inline.cpp-texpr { - font-family: unset; -} - -.sig.c .k, .sig.c .kt, -.sig.cpp .k, .sig.cpp .kt { - color: #0033B3; -} - -.sig.c .m, -.sig.cpp .m { - color: #1750EB; -} - -.sig.c .s, .sig.c .sc, -.sig.cpp .s, .sig.cpp .sc { - color: #067D17; -} - - -/* -- other body styles ----------------------------------------------------- */ - -ol.arabic { - list-style: decimal; -} - -ol.loweralpha { - list-style: lower-alpha; -} - -ol.upperalpha { - list-style: upper-alpha; -} - -ol.lowerroman { - list-style: lower-roman; -} - -ol.upperroman { - list-style: upper-roman; -} - -:not(li) > ol > li:first-child > :first-child, -:not(li) > ul > li:first-child > :first-child { - margin-top: 0px; -} - -:not(li) > ol > li:last-child > :last-child, -:not(li) > ul > li:last-child > :last-child { - margin-bottom: 0px; -} - -ol.simple ol p, -ol.simple ul p, -ul.simple ol p, -ul.simple ul p { - margin-top: 0; -} - -ol.simple > li:not(:first-child) > p, -ul.simple > li:not(:first-child) > p { - margin-top: 0; -} - -ol.simple p, -ul.simple p { - margin-bottom: 0; -} - -aside.footnote > span, -div.citation > span { - float: left; -} -aside.footnote > span:last-of-type, -div.citation > span:last-of-type { - padding-right: 0.5em; -} -aside.footnote > p { - margin-left: 2em; -} -div.citation > p { - margin-left: 4em; -} -aside.footnote > p:last-of-type, -div.citation > p:last-of-type { - margin-bottom: 0em; -} -aside.footnote > p:last-of-type:after, -div.citation > p:last-of-type:after { - content: ""; - clear: both; -} - -dl.field-list { - display: grid; - grid-template-columns: fit-content(30%) auto; -} - -dl.field-list > dt { - font-weight: bold; - word-break: break-word; - padding-left: 0.5em; - padding-right: 5px; -} - -dl.field-list > dd { - padding-left: 0.5em; - margin-top: 0em; - margin-left: 0em; - margin-bottom: 0em; -} - -dl { - margin-bottom: 15px; -} - -dd > :first-child { - margin-top: 0px; -} - -dd ul, dd table { - margin-bottom: 10px; -} - -dd { - margin-top: 3px; - margin-bottom: 10px; - margin-left: 30px; -} - -.sig dd { - margin-top: 0px; - margin-bottom: 0px; -} - -.sig dl { - margin-top: 0px; - margin-bottom: 0px; -} - -dl > dd:last-child, -dl > dd:last-child > :last-child { - margin-bottom: 0; -} - -dt:target, span.highlighted { - background-color: #fbe54e; -} - -rect.highlighted { - fill: #fbe54e; -} - -dl.glossary dt { - font-weight: bold; - font-size: 1.1em; -} - -.versionmodified { - font-style: italic; -} - -.system-message { - background-color: #fda; - padding: 5px; - border: 3px solid red; -} - -.footnote:target { - background-color: #ffa; -} - -.line-block { - display: block; - margin-top: 1em; - margin-bottom: 1em; -} - -.line-block .line-block { - margin-top: 0; - margin-bottom: 0; - margin-left: 1.5em; -} - -.guilabel, .menuselection { - font-family: sans-serif; -} - -.accelerator { - text-decoration: underline; -} - -.classifier { - font-style: oblique; -} - -.classifier:before { - font-style: normal; - margin: 0 0.5em; - content: ":"; - display: inline-block; -} - -abbr, acronym { - border-bottom: dotted 1px; - cursor: help; -} - -/* -- code displays --------------------------------------------------------- */ - -pre { - overflow: auto; - overflow-y: hidden; /* fixes display issues on Chrome browsers */ -} - -pre, div[class*="highlight-"] { - clear: both; -} - -span.pre { - -moz-hyphens: none; - -ms-hyphens: none; - -webkit-hyphens: none; - hyphens: none; - white-space: nowrap; -} - -div[class*="highlight-"] { - margin: 1em 0; -} - -td.linenos pre { - border: 0; - background-color: transparent; - color: #aaa; -} - -table.highlighttable { - display: block; -} - -table.highlighttable tbody { - display: block; -} - -table.highlighttable tr { - display: flex; -} - -table.highlighttable td { - margin: 0; - padding: 0; -} - -table.highlighttable td.linenos { - padding-right: 0.5em; -} - -table.highlighttable td.code { - flex: 1; - overflow: hidden; -} - -.highlight .hll { - display: block; -} - -div.highlight pre, -table.highlighttable pre { - margin: 0; -} - -div.code-block-caption + div { - margin-top: 0; -} - -div.code-block-caption { - margin-top: 1em; - padding: 2px 5px; - font-size: small; -} - -div.code-block-caption code { - background-color: transparent; -} - -table.highlighttable td.linenos, -span.linenos, -div.highlight span.gp { /* gp: Generic.Prompt */ - user-select: none; - -webkit-user-select: text; /* Safari fallback only */ - -webkit-user-select: none; /* Chrome/Safari */ - -moz-user-select: none; /* Firefox */ - -ms-user-select: none; /* IE10+ */ -} - -div.code-block-caption span.caption-number { - padding: 0.1em 0.3em; - font-style: italic; -} - -div.code-block-caption span.caption-text { -} - -div.literal-block-wrapper { - margin: 1em 0; -} - -code.xref, a code { - background-color: transparent; - font-weight: bold; -} - -h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { - background-color: transparent; -} - -.viewcode-link { - float: right; -} - -.viewcode-back { - float: right; - font-family: sans-serif; -} - -div.viewcode-block:target { - margin: -1px -10px; - padding: 0 10px; -} - -/* -- math display ---------------------------------------------------------- */ - -img.math { - vertical-align: middle; -} - -div.body div.math p { - text-align: center; -} - -span.eqno { - float: right; -} - -span.eqno a.headerlink { - position: absolute; - z-index: 1; -} - -div.math:hover a.headerlink { - visibility: visible; -} - -/* -- printout stylesheet --------------------------------------------------- */ - -@media print { - div.document, - div.documentwrapper, - div.bodywrapper { - margin: 0 !important; - width: 100%; - } - - div.sphinxsidebar, - div.related, - div.footer, - #top-link { - display: none; - } -} \ No newline at end of file diff --git a/docs_local/_build/_static/custom.css b/docs_local/_build/_static/custom.css deleted file mode 100644 index 2a924f1d..00000000 --- a/docs_local/_build/_static/custom.css +++ /dev/null @@ -1 +0,0 @@ -/* This file intentionally left blank. */ diff --git a/docs_local/_build/_static/doctools.js b/docs_local/_build/_static/doctools.js deleted file mode 100644 index 807cdb17..00000000 --- a/docs_local/_build/_static/doctools.js +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Base JavaScript utilities for all Sphinx HTML documentation. - */ -"use strict"; - -const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ - "TEXTAREA", - "INPUT", - "SELECT", - "BUTTON", -]); - -const _ready = (callback) => { - if (document.readyState !== "loading") { - callback(); - } else { - document.addEventListener("DOMContentLoaded", callback); - } -}; - -/** - * Small JavaScript module for the documentation. - */ -const Documentation = { - init: () => { - Documentation.initDomainIndexTable(); - Documentation.initOnKeyListeners(); - }, - - /** - * i18n support - */ - TRANSLATIONS: {}, - PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), - LOCALE: "unknown", - - // gettext and ngettext don't access this so that the functions - // can safely bound to a different name (_ = Documentation.gettext) - gettext: (string) => { - const translated = Documentation.TRANSLATIONS[string]; - switch (typeof translated) { - case "undefined": - return string; // no translation - case "string": - return translated; // translation exists - default: - return translated[0]; // (singular, plural) translation tuple exists - } - }, - - ngettext: (singular, plural, n) => { - const translated = Documentation.TRANSLATIONS[singular]; - if (typeof translated !== "undefined") - return translated[Documentation.PLURAL_EXPR(n)]; - return n === 1 ? singular : plural; - }, - - addTranslations: (catalog) => { - Object.assign(Documentation.TRANSLATIONS, catalog.messages); - Documentation.PLURAL_EXPR = new Function( - "n", - `return (${catalog.plural_expr})`, - ); - Documentation.LOCALE = catalog.locale; - }, - - /** - * helper function to focus on search bar - */ - focusSearchBar: () => { - document.querySelectorAll("input[name=q]")[0]?.focus(); - }, - - /** - * Initialise the domain index toggle buttons - */ - initDomainIndexTable: () => { - const toggler = (el) => { - const idNumber = el.id.substr(7); - const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); - if (el.src.substr(-9) === "minus.png") { - el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; - toggledRows.forEach((el) => (el.style.display = "none")); - } else { - el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; - toggledRows.forEach((el) => (el.style.display = "")); - } - }; - - const togglerElements = document.querySelectorAll("img.toggler"); - togglerElements.forEach((el) => - el.addEventListener("click", (event) => toggler(event.currentTarget)), - ); - togglerElements.forEach((el) => (el.style.display = "")); - if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); - }, - - initOnKeyListeners: () => { - // only install a listener if it is really needed - if ( - !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS - && !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS - ) - return; - - document.addEventListener("keydown", (event) => { - // bail for input elements - if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) - return; - // bail with special keys - if (event.altKey || event.ctrlKey || event.metaKey) return; - - if (!event.shiftKey) { - switch (event.key) { - case "ArrowLeft": - if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; - - const prevLink = document.querySelector('link[rel="prev"]'); - if (prevLink && prevLink.href) { - window.location.href = prevLink.href; - event.preventDefault(); - } - break; - case "ArrowRight": - if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; - - const nextLink = document.querySelector('link[rel="next"]'); - if (nextLink && nextLink.href) { - window.location.href = nextLink.href; - event.preventDefault(); - } - break; - } - } - - // some keyboard layouts may need Shift to get / - switch (event.key) { - case "/": - if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; - Documentation.focusSearchBar(); - event.preventDefault(); - } - }); - }, -}; - -// quick alias for translations -const _ = Documentation.gettext; - -_ready(Documentation.init); diff --git a/docs_local/_build/_static/documentation_options.js b/docs_local/_build/_static/documentation_options.js deleted file mode 100644 index e8b7cc1a..00000000 --- a/docs_local/_build/_static/documentation_options.js +++ /dev/null @@ -1,13 +0,0 @@ -const DOCUMENTATION_OPTIONS = { - VERSION: '0.1.0b11', - LANGUAGE: 'en', - COLLAPSE_INDEX: false, - BUILDER: 'html', - FILE_SUFFIX: '.html', - LINK_SUFFIX: '.html', - HAS_SOURCE: true, - SOURCELINK_SUFFIX: '.txt', - NAVIGATION_WITH_KEYS: false, - SHOW_SEARCH_SUMMARY: true, - ENABLE_SEARCH_SHORTCUTS: true, -}; \ No newline at end of file diff --git a/docs_local/_build/_static/english-stemmer.js b/docs_local/_build/_static/english-stemmer.js deleted file mode 100644 index 056760ee..00000000 --- a/docs_local/_build/_static/english-stemmer.js +++ /dev/null @@ -1,1066 +0,0 @@ -// Generated from english.sbl by Snowball 3.0.1 - https://snowballstem.org/ - -/**@constructor*/ -var EnglishStemmer = function() { - var base = new BaseStemmer(); - - /** @const */ var a_0 = [ - ["arsen", -1, -1], - ["commun", -1, -1], - ["emerg", -1, -1], - ["gener", -1, -1], - ["later", -1, -1], - ["organ", -1, -1], - ["past", -1, -1], - ["univers", -1, -1] - ]; - - /** @const */ var a_1 = [ - ["'", -1, 1], - ["'s'", 0, 1], - ["'s", -1, 1] - ]; - - /** @const */ var a_2 = [ - ["ied", -1, 2], - ["s", -1, 3], - ["ies", 1, 2], - ["sses", 1, 1], - ["ss", 1, -1], - ["us", 1, -1] - ]; - - /** @const */ var a_3 = [ - ["succ", -1, 1], - ["proc", -1, 1], - ["exc", -1, 1] - ]; - - /** @const */ var a_4 = [ - ["even", -1, 2], - ["cann", -1, 2], - ["inn", -1, 2], - ["earr", -1, 2], - ["herr", -1, 2], - ["out", -1, 2], - ["y", -1, 1] - ]; - - /** @const */ var a_5 = [ - ["", -1, -1], - ["ed", 0, 2], - ["eed", 1, 1], - ["ing", 0, 3], - ["edly", 0, 2], - ["eedly", 4, 1], - ["ingly", 0, 2] - ]; - - /** @const */ var a_6 = [ - ["", -1, 3], - ["bb", 0, 2], - ["dd", 0, 2], - ["ff", 0, 2], - ["gg", 0, 2], - ["bl", 0, 1], - ["mm", 0, 2], - ["nn", 0, 2], - ["pp", 0, 2], - ["rr", 0, 2], - ["at", 0, 1], - ["tt", 0, 2], - ["iz", 0, 1] - ]; - - /** @const */ var a_7 = [ - ["anci", -1, 3], - ["enci", -1, 2], - ["ogi", -1, 14], - ["li", -1, 16], - ["bli", 3, 12], - ["abli", 4, 4], - ["alli", 3, 8], - ["fulli", 3, 9], - ["lessli", 3, 15], - ["ousli", 3, 10], - ["entli", 3, 5], - ["aliti", -1, 8], - ["biliti", -1, 12], - ["iviti", -1, 11], - ["tional", -1, 1], - ["ational", 14, 7], - ["alism", -1, 8], - ["ation", -1, 7], - ["ization", 17, 6], - ["izer", -1, 6], - ["ator", -1, 7], - ["iveness", -1, 11], - ["fulness", -1, 9], - ["ousness", -1, 10], - ["ogist", -1, 13] - ]; - - /** @const */ var a_8 = [ - ["icate", -1, 4], - ["ative", -1, 6], - ["alize", -1, 3], - ["iciti", -1, 4], - ["ical", -1, 4], - ["tional", -1, 1], - ["ational", 5, 2], - ["ful", -1, 5], - ["ness", -1, 5] - ]; - - /** @const */ var a_9 = [ - ["ic", -1, 1], - ["ance", -1, 1], - ["ence", -1, 1], - ["able", -1, 1], - ["ible", -1, 1], - ["ate", -1, 1], - ["ive", -1, 1], - ["ize", -1, 1], - ["iti", -1, 1], - ["al", -1, 1], - ["ism", -1, 1], - ["ion", -1, 2], - ["er", -1, 1], - ["ous", -1, 1], - ["ant", -1, 1], - ["ent", -1, 1], - ["ment", 15, 1], - ["ement", 16, 1] - ]; - - /** @const */ var a_10 = [ - ["e", -1, 1], - ["l", -1, 2] - ]; - - /** @const */ var a_11 = [ - ["andes", -1, -1], - ["atlas", -1, -1], - ["bias", -1, -1], - ["cosmos", -1, -1], - ["early", -1, 5], - ["gently", -1, 3], - ["howe", -1, -1], - ["idly", -1, 2], - ["news", -1, -1], - ["only", -1, 6], - ["singly", -1, 7], - ["skies", -1, 1], - ["sky", -1, -1], - ["ugly", -1, 4] - ]; - - /** @const */ var /** Array */ g_aeo = [17, 64]; - - /** @const */ var /** Array */ g_v = [17, 65, 16, 1]; - - /** @const */ var /** Array */ g_v_WXY = [1, 17, 65, 208, 1]; - - /** @const */ var /** Array */ g_valid_LI = [55, 141, 2]; - - var /** boolean */ B_Y_found = false; - var /** number */ I_p2 = 0; - var /** number */ I_p1 = 0; - - - /** @return {boolean} */ - function r_prelude() { - B_Y_found = false; - /** @const */ var /** number */ v_1 = base.cursor; - lab0: { - base.bra = base.cursor; - if (!(base.eq_s("'"))) - { - break lab0; - } - base.ket = base.cursor; - if (!base.slice_del()) - { - return false; - } - } - base.cursor = v_1; - /** @const */ var /** number */ v_2 = base.cursor; - lab1: { - base.bra = base.cursor; - if (!(base.eq_s("y"))) - { - break lab1; - } - base.ket = base.cursor; - if (!base.slice_from("Y")) - { - return false; - } - B_Y_found = true; - } - base.cursor = v_2; - /** @const */ var /** number */ v_3 = base.cursor; - lab2: { - while(true) - { - /** @const */ var /** number */ v_4 = base.cursor; - lab3: { - golab4: while(true) - { - /** @const */ var /** number */ v_5 = base.cursor; - lab5: { - if (!(base.in_grouping(g_v, 97, 121))) - { - break lab5; - } - base.bra = base.cursor; - if (!(base.eq_s("y"))) - { - break lab5; - } - base.ket = base.cursor; - base.cursor = v_5; - break golab4; - } - base.cursor = v_5; - if (base.cursor >= base.limit) - { - break lab3; - } - base.cursor++; - } - if (!base.slice_from("Y")) - { - return false; - } - B_Y_found = true; - continue; - } - base.cursor = v_4; - break; - } - } - base.cursor = v_3; - return true; - }; - - /** @return {boolean} */ - function r_mark_regions() { - I_p1 = base.limit; - I_p2 = base.limit; - /** @const */ var /** number */ v_1 = base.cursor; - lab0: { - lab1: { - /** @const */ var /** number */ v_2 = base.cursor; - lab2: { - if (base.find_among(a_0) == 0) - { - break lab2; - } - break lab1; - } - base.cursor = v_2; - if (!base.go_out_grouping(g_v, 97, 121)) - { - break lab0; - } - base.cursor++; - if (!base.go_in_grouping(g_v, 97, 121)) - { - break lab0; - } - base.cursor++; - } - I_p1 = base.cursor; - if (!base.go_out_grouping(g_v, 97, 121)) - { - break lab0; - } - base.cursor++; - if (!base.go_in_grouping(g_v, 97, 121)) - { - break lab0; - } - base.cursor++; - I_p2 = base.cursor; - } - base.cursor = v_1; - return true; - }; - - /** @return {boolean} */ - function r_shortv() { - lab0: { - /** @const */ var /** number */ v_1 = base.limit - base.cursor; - lab1: { - if (!(base.out_grouping_b(g_v_WXY, 89, 121))) - { - break lab1; - } - if (!(base.in_grouping_b(g_v, 97, 121))) - { - break lab1; - } - if (!(base.out_grouping_b(g_v, 97, 121))) - { - break lab1; - } - break lab0; - } - base.cursor = base.limit - v_1; - lab2: { - if (!(base.out_grouping_b(g_v, 97, 121))) - { - break lab2; - } - if (!(base.in_grouping_b(g_v, 97, 121))) - { - break lab2; - } - if (base.cursor > base.limit_backward) - { - break lab2; - } - break lab0; - } - base.cursor = base.limit - v_1; - if (!(base.eq_s_b("past"))) - { - return false; - } - } - return true; - }; - - /** @return {boolean} */ - function r_R1() { - return I_p1 <= base.cursor; - }; - - /** @return {boolean} */ - function r_R2() { - return I_p2 <= base.cursor; - }; - - /** @return {boolean} */ - function r_Step_1a() { - var /** number */ among_var; - /** @const */ var /** number */ v_1 = base.limit - base.cursor; - lab0: { - base.ket = base.cursor; - if (base.find_among_b(a_1) == 0) - { - base.cursor = base.limit - v_1; - break lab0; - } - base.bra = base.cursor; - if (!base.slice_del()) - { - return false; - } - } - base.ket = base.cursor; - among_var = base.find_among_b(a_2); - if (among_var == 0) - { - return false; - } - base.bra = base.cursor; - switch (among_var) { - case 1: - if (!base.slice_from("ss")) - { - return false; - } - break; - case 2: - lab1: { - /** @const */ var /** number */ v_2 = base.limit - base.cursor; - lab2: { - { - /** @const */ var /** number */ c1 = base.cursor - 2; - if (c1 < base.limit_backward) - { - break lab2; - } - base.cursor = c1; - } - if (!base.slice_from("i")) - { - return false; - } - break lab1; - } - base.cursor = base.limit - v_2; - if (!base.slice_from("ie")) - { - return false; - } - } - break; - case 3: - if (base.cursor <= base.limit_backward) - { - return false; - } - base.cursor--; - if (!base.go_out_grouping_b(g_v, 97, 121)) - { - return false; - } - base.cursor--; - if (!base.slice_del()) - { - return false; - } - break; - } - return true; - }; - - /** @return {boolean} */ - function r_Step_1b() { - var /** number */ among_var; - base.ket = base.cursor; - among_var = base.find_among_b(a_5); - base.bra = base.cursor; - lab0: { - /** @const */ var /** number */ v_1 = base.limit - base.cursor; - lab1: { - switch (among_var) { - case 1: - /** @const */ var /** number */ v_2 = base.limit - base.cursor; - lab2: { - lab3: { - /** @const */ var /** number */ v_3 = base.limit - base.cursor; - lab4: { - if (base.find_among_b(a_3) == 0) - { - break lab4; - } - if (base.cursor > base.limit_backward) - { - break lab4; - } - break lab3; - } - base.cursor = base.limit - v_3; - if (!r_R1()) - { - break lab2; - } - if (!base.slice_from("ee")) - { - return false; - } - } - } - base.cursor = base.limit - v_2; - break; - case 2: - break lab1; - case 3: - among_var = base.find_among_b(a_4); - if (among_var == 0) - { - break lab1; - } - switch (among_var) { - case 1: - /** @const */ var /** number */ v_4 = base.limit - base.cursor; - if (!(base.out_grouping_b(g_v, 97, 121))) - { - break lab1; - } - if (base.cursor > base.limit_backward) - { - break lab1; - } - base.cursor = base.limit - v_4; - base.bra = base.cursor; - if (!base.slice_from("ie")) - { - return false; - } - break; - case 2: - if (base.cursor > base.limit_backward) - { - break lab1; - } - break; - } - break; - } - break lab0; - } - base.cursor = base.limit - v_1; - /** @const */ var /** number */ v_5 = base.limit - base.cursor; - if (!base.go_out_grouping_b(g_v, 97, 121)) - { - return false; - } - base.cursor--; - base.cursor = base.limit - v_5; - if (!base.slice_del()) - { - return false; - } - base.ket = base.cursor; - base.bra = base.cursor; - /** @const */ var /** number */ v_6 = base.limit - base.cursor; - among_var = base.find_among_b(a_6); - switch (among_var) { - case 1: - if (!base.slice_from("e")) - { - return false; - } - return false; - case 2: - { - /** @const */ var /** number */ v_7 = base.limit - base.cursor; - lab5: { - if (!(base.in_grouping_b(g_aeo, 97, 111))) - { - break lab5; - } - if (base.cursor > base.limit_backward) - { - break lab5; - } - return false; - } - base.cursor = base.limit - v_7; - } - break; - case 3: - if (base.cursor != I_p1) - { - return false; - } - /** @const */ var /** number */ v_8 = base.limit - base.cursor; - if (!r_shortv()) - { - return false; - } - base.cursor = base.limit - v_8; - if (!base.slice_from("e")) - { - return false; - } - return false; - } - base.cursor = base.limit - v_6; - base.ket = base.cursor; - if (base.cursor <= base.limit_backward) - { - return false; - } - base.cursor--; - base.bra = base.cursor; - if (!base.slice_del()) - { - return false; - } - } - return true; - }; - - /** @return {boolean} */ - function r_Step_1c() { - base.ket = base.cursor; - lab0: { - /** @const */ var /** number */ v_1 = base.limit - base.cursor; - lab1: { - if (!(base.eq_s_b("y"))) - { - break lab1; - } - break lab0; - } - base.cursor = base.limit - v_1; - if (!(base.eq_s_b("Y"))) - { - return false; - } - } - base.bra = base.cursor; - if (!(base.out_grouping_b(g_v, 97, 121))) - { - return false; - } - lab2: { - if (base.cursor > base.limit_backward) - { - break lab2; - } - return false; - } - if (!base.slice_from("i")) - { - return false; - } - return true; - }; - - /** @return {boolean} */ - function r_Step_2() { - var /** number */ among_var; - base.ket = base.cursor; - among_var = base.find_among_b(a_7); - if (among_var == 0) - { - return false; - } - base.bra = base.cursor; - if (!r_R1()) - { - return false; - } - switch (among_var) { - case 1: - if (!base.slice_from("tion")) - { - return false; - } - break; - case 2: - if (!base.slice_from("ence")) - { - return false; - } - break; - case 3: - if (!base.slice_from("ance")) - { - return false; - } - break; - case 4: - if (!base.slice_from("able")) - { - return false; - } - break; - case 5: - if (!base.slice_from("ent")) - { - return false; - } - break; - case 6: - if (!base.slice_from("ize")) - { - return false; - } - break; - case 7: - if (!base.slice_from("ate")) - { - return false; - } - break; - case 8: - if (!base.slice_from("al")) - { - return false; - } - break; - case 9: - if (!base.slice_from("ful")) - { - return false; - } - break; - case 10: - if (!base.slice_from("ous")) - { - return false; - } - break; - case 11: - if (!base.slice_from("ive")) - { - return false; - } - break; - case 12: - if (!base.slice_from("ble")) - { - return false; - } - break; - case 13: - if (!base.slice_from("og")) - { - return false; - } - break; - case 14: - if (!(base.eq_s_b("l"))) - { - return false; - } - if (!base.slice_from("og")) - { - return false; - } - break; - case 15: - if (!base.slice_from("less")) - { - return false; - } - break; - case 16: - if (!(base.in_grouping_b(g_valid_LI, 99, 116))) - { - return false; - } - if (!base.slice_del()) - { - return false; - } - break; - } - return true; - }; - - /** @return {boolean} */ - function r_Step_3() { - var /** number */ among_var; - base.ket = base.cursor; - among_var = base.find_among_b(a_8); - if (among_var == 0) - { - return false; - } - base.bra = base.cursor; - if (!r_R1()) - { - return false; - } - switch (among_var) { - case 1: - if (!base.slice_from("tion")) - { - return false; - } - break; - case 2: - if (!base.slice_from("ate")) - { - return false; - } - break; - case 3: - if (!base.slice_from("al")) - { - return false; - } - break; - case 4: - if (!base.slice_from("ic")) - { - return false; - } - break; - case 5: - if (!base.slice_del()) - { - return false; - } - break; - case 6: - if (!r_R2()) - { - return false; - } - if (!base.slice_del()) - { - return false; - } - break; - } - return true; - }; - - /** @return {boolean} */ - function r_Step_4() { - var /** number */ among_var; - base.ket = base.cursor; - among_var = base.find_among_b(a_9); - if (among_var == 0) - { - return false; - } - base.bra = base.cursor; - if (!r_R2()) - { - return false; - } - switch (among_var) { - case 1: - if (!base.slice_del()) - { - return false; - } - break; - case 2: - lab0: { - /** @const */ var /** number */ v_1 = base.limit - base.cursor; - lab1: { - if (!(base.eq_s_b("s"))) - { - break lab1; - } - break lab0; - } - base.cursor = base.limit - v_1; - if (!(base.eq_s_b("t"))) - { - return false; - } - } - if (!base.slice_del()) - { - return false; - } - break; - } - return true; - }; - - /** @return {boolean} */ - function r_Step_5() { - var /** number */ among_var; - base.ket = base.cursor; - among_var = base.find_among_b(a_10); - if (among_var == 0) - { - return false; - } - base.bra = base.cursor; - switch (among_var) { - case 1: - lab0: { - lab1: { - if (!r_R2()) - { - break lab1; - } - break lab0; - } - if (!r_R1()) - { - return false; - } - { - /** @const */ var /** number */ v_1 = base.limit - base.cursor; - lab2: { - if (!r_shortv()) - { - break lab2; - } - return false; - } - base.cursor = base.limit - v_1; - } - } - if (!base.slice_del()) - { - return false; - } - break; - case 2: - if (!r_R2()) - { - return false; - } - if (!(base.eq_s_b("l"))) - { - return false; - } - if (!base.slice_del()) - { - return false; - } - break; - } - return true; - }; - - /** @return {boolean} */ - function r_exception1() { - var /** number */ among_var; - base.bra = base.cursor; - among_var = base.find_among(a_11); - if (among_var == 0) - { - return false; - } - base.ket = base.cursor; - if (base.cursor < base.limit) - { - return false; - } - switch (among_var) { - case 1: - if (!base.slice_from("sky")) - { - return false; - } - break; - case 2: - if (!base.slice_from("idl")) - { - return false; - } - break; - case 3: - if (!base.slice_from("gentl")) - { - return false; - } - break; - case 4: - if (!base.slice_from("ugli")) - { - return false; - } - break; - case 5: - if (!base.slice_from("earli")) - { - return false; - } - break; - case 6: - if (!base.slice_from("onli")) - { - return false; - } - break; - case 7: - if (!base.slice_from("singl")) - { - return false; - } - break; - } - return true; - }; - - /** @return {boolean} */ - function r_postlude() { - if (!B_Y_found) - { - return false; - } - while(true) - { - /** @const */ var /** number */ v_1 = base.cursor; - lab0: { - golab1: while(true) - { - /** @const */ var /** number */ v_2 = base.cursor; - lab2: { - base.bra = base.cursor; - if (!(base.eq_s("Y"))) - { - break lab2; - } - base.ket = base.cursor; - base.cursor = v_2; - break golab1; - } - base.cursor = v_2; - if (base.cursor >= base.limit) - { - break lab0; - } - base.cursor++; - } - if (!base.slice_from("y")) - { - return false; - } - continue; - } - base.cursor = v_1; - break; - } - return true; - }; - - this.stem = /** @return {boolean} */ function() { - lab0: { - /** @const */ var /** number */ v_1 = base.cursor; - lab1: { - if (!r_exception1()) - { - break lab1; - } - break lab0; - } - base.cursor = v_1; - lab2: { - { - /** @const */ var /** number */ v_2 = base.cursor; - lab3: { - { - /** @const */ var /** number */ c1 = base.cursor + 3; - if (c1 > base.limit) - { - break lab3; - } - base.cursor = c1; - } - break lab2; - } - base.cursor = v_2; - } - break lab0; - } - base.cursor = v_1; - r_prelude(); - r_mark_regions(); - base.limit_backward = base.cursor; base.cursor = base.limit; - /** @const */ var /** number */ v_3 = base.limit - base.cursor; - r_Step_1a(); - base.cursor = base.limit - v_3; - /** @const */ var /** number */ v_4 = base.limit - base.cursor; - r_Step_1b(); - base.cursor = base.limit - v_4; - /** @const */ var /** number */ v_5 = base.limit - base.cursor; - r_Step_1c(); - base.cursor = base.limit - v_5; - /** @const */ var /** number */ v_6 = base.limit - base.cursor; - r_Step_2(); - base.cursor = base.limit - v_6; - /** @const */ var /** number */ v_7 = base.limit - base.cursor; - r_Step_3(); - base.cursor = base.limit - v_7; - /** @const */ var /** number */ v_8 = base.limit - base.cursor; - r_Step_4(); - base.cursor = base.limit - v_8; - /** @const */ var /** number */ v_9 = base.limit - base.cursor; - r_Step_5(); - base.cursor = base.limit - v_9; - base.cursor = base.limit_backward; - /** @const */ var /** number */ v_10 = base.cursor; - r_postlude(); - base.cursor = v_10; - } - return true; - }; - - /**@return{string}*/ - this['stemWord'] = function(/**string*/word) { - base.setCurrent(word); - this.stem(); - return base.getCurrent(); - }; -}; diff --git a/docs_local/_build/_static/file.png b/docs_local/_build/_static/file.png deleted file mode 100644 index a858a410e4faa62ce324d814e4b816fff83a6fb3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 286 zcmV+(0pb3MP)s`hMrGg#P~ix$^RISR_I47Y|r1 z_CyJOe}D1){SET-^Amu_i71Lt6eYfZjRyw@I6OQAIXXHDfiX^GbOlHe=Ae4>0m)d(f|Me07*qoM6N<$f}vM^LjV8( diff --git a/docs_local/_build/_static/graphviz.css b/docs_local/_build/_static/graphviz.css deleted file mode 100644 index 30f3837b..00000000 --- a/docs_local/_build/_static/graphviz.css +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Sphinx stylesheet -- graphviz extension. - */ - -img.graphviz { - border: 0; - max-width: 100%; -} - -object.graphviz { - max-width: 100%; -} diff --git a/docs_local/_build/_static/language_data.js b/docs_local/_build/_static/language_data.js deleted file mode 100644 index 57767864..00000000 --- a/docs_local/_build/_static/language_data.js +++ /dev/null @@ -1,13 +0,0 @@ -/* - * This script contains the language-specific data used by searchtools.js, - * namely the set of stopwords, stemmer, scorer and splitter. - */ - -const stopwords = new Set(["a", "about", "above", "after", "again", "against", "all", "am", "an", "and", "any", "are", "aren't", "as", "at", "be", "because", "been", "before", "being", "below", "between", "both", "but", "by", "can't", "cannot", "could", "couldn't", "did", "didn't", "do", "does", "doesn't", "doing", "don't", "down", "during", "each", "few", "for", "from", "further", "had", "hadn't", "has", "hasn't", "have", "haven't", "having", "he", "he'd", "he'll", "he's", "her", "here", "here's", "hers", "herself", "him", "himself", "his", "how", "how's", "i", "i'd", "i'll", "i'm", "i've", "if", "in", "into", "is", "isn't", "it", "it's", "its", "itself", "let's", "me", "more", "most", "mustn't", "my", "myself", "no", "nor", "not", "of", "off", "on", "once", "only", "or", "other", "ought", "our", "ours", "ourselves", "out", "over", "own", "same", "shan't", "she", "she'd", "she'll", "she's", "should", "shouldn't", "so", "some", "such", "than", "that", "that's", "the", "their", "theirs", "them", "themselves", "then", "there", "there's", "these", "they", "they'd", "they'll", "they're", "they've", "this", "those", "through", "to", "too", "under", "until", "up", "very", "was", "wasn't", "we", "we'd", "we'll", "we're", "we've", "were", "weren't", "what", "what's", "when", "when's", "where", "where's", "which", "while", "who", "who's", "whom", "why", "why's", "with", "won't", "would", "wouldn't", "you", "you'd", "you'll", "you're", "you've", "your", "yours", "yourself", "yourselves"]); -window.stopwords = stopwords; // Export to global scope - - -/* Non-minified versions are copied as separate JavaScript files, if available */ -BaseStemmer=function(){this.current="",this.cursor=0,this.limit=0,this.limit_backward=0,this.bra=0,this.ket=0,this.setCurrent=function(t){this.current=t,this.cursor=0,this.limit=this.current.length,this.limit_backward=0,this.bra=this.cursor,this.ket=this.limit},this.getCurrent=function(){return this.current},this.copy_from=function(t){this.current=t.current,this.cursor=t.cursor,this.limit=t.limit,this.limit_backward=t.limit_backward,this.bra=t.bra,this.ket=t.ket},this.in_grouping=function(t,r,i){return!(this.cursor>=this.limit||i<(i=this.current.charCodeAt(this.cursor))||i>>3]&1<<(7&i))||(this.cursor++,0))},this.go_in_grouping=function(t,r,i){for(;this.cursor>>3]&1<<(7&s)))return!0;this.cursor++}return!1},this.in_grouping_b=function(t,r,i){return!(this.cursor<=this.limit_backward||i<(i=this.current.charCodeAt(this.cursor-1))||i>>3]&1<<(7&i))||(this.cursor--,0))},this.go_in_grouping_b=function(t,r,i){for(;this.cursor>this.limit_backward;){var s=this.current.charCodeAt(this.cursor-1);if(i>>3]&1<<(7&s)))return!0;this.cursor--}return!1},this.out_grouping=function(t,r,i){return!(this.cursor>=this.limit)&&(i<(i=this.current.charCodeAt(this.cursor))||i>>3]&1<<(7&i)))&&(this.cursor++,!0)},this.go_out_grouping=function(t,r,i){for(;this.cursor>>3]&1<<(7&s)))return!0;this.cursor++}return!1},this.out_grouping_b=function(t,r,i){return!(this.cursor<=this.limit_backward)&&(i<(i=this.current.charCodeAt(this.cursor-1))||i>>3]&1<<(7&i)))&&(this.cursor--,!0)},this.go_out_grouping_b=function(t,r,i){for(;this.cursor>this.limit_backward;){var s=this.current.charCodeAt(this.cursor-1);if(s<=i&&r<=s&&0!=(t[(s-=r)>>>3]&1<<(7&s)))return!0;this.cursor--}return!1},this.eq_s=function(t){return!(this.limit-this.cursor>>1),o=0,a=e=(l=t[r])[0].length){if(this.cursor=s+l[0].length,l.length<4)return l[2];var g=l[3](this);if(this.cursor=s+l[0].length,g)return l[2]}}while(0<=(r=l[1]));return 0},this.find_among_b=function(t){for(var r=0,i=t.length,s=this.cursor,h=this.limit_backward,e=0,n=0,c=!1;;){for(var u,o=r+(i-r>>1),a=0,l=e=(u=t[r])[0].length){if(this.cursor=s-u[0].length,u.length<4)return u[2];var g=u[3](this);if(this.cursor=s-u[0].length,g)return u[2]}}while(0<=(r=u[1]));return 0},this.replace_s=function(t,r,i){var s=i.length-(r-t);return this.current=this.current.slice(0,t)+i+this.current.slice(r),this.limit+=s,this.cursor>=r?this.cursor+=s:this.cursor>t&&(this.cursor=t),s},this.slice_check=function(){return!(this.bra<0||this.bra>this.ket||this.ket>this.limit||this.limit>this.current.length)},this.slice_from=function(t){var r=!1;return this.slice_check()&&(this.replace_s(this.bra,this.ket,t),r=!0),r},this.slice_del=function(){return this.slice_from("")},this.insert=function(t,r,i){r=this.replace_s(t,r,i);t<=this.bra&&(this.bra+=r),t<=this.ket&&(this.ket+=r)},this.slice_to=function(){var t="";return t=this.slice_check()?this.current.slice(this.bra,this.ket):t},this.assign_to=function(){return this.current.slice(0,this.limit)}}; -var EnglishStemmer=function(){var a=new BaseStemmer,c=[["arsen",-1,-1],["commun",-1,-1],["emerg",-1,-1],["gener",-1,-1],["later",-1,-1],["organ",-1,-1],["past",-1,-1],["univers",-1,-1]],o=[["'",-1,1],["'s'",0,1],["'s",-1,1]],u=[["ied",-1,2],["s",-1,3],["ies",1,2],["sses",1,1],["ss",1,-1],["us",1,-1]],t=[["succ",-1,1],["proc",-1,1],["exc",-1,1]],l=[["even",-1,2],["cann",-1,2],["inn",-1,2],["earr",-1,2],["herr",-1,2],["out",-1,2],["y",-1,1]],n=[["",-1,-1],["ed",0,2],["eed",1,1],["ing",0,3],["edly",0,2],["eedly",4,1],["ingly",0,2]],f=[["",-1,3],["bb",0,2],["dd",0,2],["ff",0,2],["gg",0,2],["bl",0,1],["mm",0,2],["nn",0,2],["pp",0,2],["rr",0,2],["at",0,1],["tt",0,2],["iz",0,1]],_=[["anci",-1,3],["enci",-1,2],["ogi",-1,14],["li",-1,16],["bli",3,12],["abli",4,4],["alli",3,8],["fulli",3,9],["lessli",3,15],["ousli",3,10],["entli",3,5],["aliti",-1,8],["biliti",-1,12],["iviti",-1,11],["tional",-1,1],["ational",14,7],["alism",-1,8],["ation",-1,7],["ization",17,6],["izer",-1,6],["ator",-1,7],["iveness",-1,11],["fulness",-1,9],["ousness",-1,10],["ogist",-1,13]],m=[["icate",-1,4],["ative",-1,6],["alize",-1,3],["iciti",-1,4],["ical",-1,4],["tional",-1,1],["ational",5,2],["ful",-1,5],["ness",-1,5]],b=[["ic",-1,1],["ance",-1,1],["ence",-1,1],["able",-1,1],["ible",-1,1],["ate",-1,1],["ive",-1,1],["ize",-1,1],["iti",-1,1],["al",-1,1],["ism",-1,1],["ion",-1,2],["er",-1,1],["ous",-1,1],["ant",-1,1],["ent",-1,1],["ment",15,1],["ement",16,1]],k=[["e",-1,1],["l",-1,2]],g=[["andes",-1,-1],["atlas",-1,-1],["bias",-1,-1],["cosmos",-1,-1],["early",-1,5],["gently",-1,3],["howe",-1,-1],["idly",-1,2],["news",-1,-1],["only",-1,6],["singly",-1,7],["skies",-1,1],["sky",-1,-1],["ugly",-1,4]],d=[17,64],v=[17,65,16,1],i=[1,17,65,208,1],w=[55,141,2],p=!1,y=0,h=0;function q(){var r=a.limit-a.cursor;return!!(a.out_grouping_b(i,89,121)&&a.in_grouping_b(v,97,121)&&a.out_grouping_b(v,97,121)||(a.cursor=a.limit-r,a.out_grouping_b(v,97,121)&&a.in_grouping_b(v,97,121)&&!(a.cursor>a.limit_backward))||(a.cursor=a.limit-r,a.eq_s_b("past")))}function z(){return h<=a.cursor}function Y(){return y<=a.cursor}this.stem=function(){var r=a.cursor;if(!(()=>{var r;if(a.bra=a.cursor,0!=(r=a.find_among(g))&&(a.ket=a.cursor,!(a.cursora.limit)a.cursor=i;else{a.cursor=e,a.cursor=r,(()=>{p=!1;var r=a.cursor;if(a.bra=a.cursor,!a.eq_s("'")||(a.ket=a.cursor,a.slice_del())){a.cursor=r;r=a.cursor;if(a.bra=a.cursor,a.eq_s("y")){if(a.ket=a.cursor,!a.slice_from("Y"))return;p=!0}a.cursor=r;for(r=a.cursor;;){var i=a.cursor;r:{for(;;){var e=a.cursor;if(a.in_grouping(v,97,121)&&(a.bra=a.cursor,a.eq_s("y"))){a.ket=a.cursor,a.cursor=e;break}if(a.cursor=e,a.cursor>=a.limit)break r;a.cursor++}if(!a.slice_from("Y"))return;p=!0;continue}a.cursor=i;break}a.cursor=r}})(),h=a.limit,y=a.limit;i=a.cursor;r:{var s=a.cursor;if(0==a.find_among(c)){if(a.cursor=s,!a.go_out_grouping(v,97,121))break r;if(a.cursor++,!a.go_in_grouping(v,97,121))break r;a.cursor++}h=a.cursor,a.go_out_grouping(v,97,121)&&(a.cursor++,a.go_in_grouping(v,97,121))&&(a.cursor++,y=a.cursor)}a.cursor=i,a.limit_backward=a.cursor,a.cursor=a.limit;var e=a.limit-a.cursor,r=((()=>{var r=a.limit-a.cursor;if(a.ket=a.cursor,0==a.find_among_b(o))a.cursor=a.limit-r;else if(a.bra=a.cursor,!a.slice_del())return;if(a.ket=a.cursor,0!=(r=a.find_among_b(u)))switch(a.bra=a.cursor,r){case 1:if(a.slice_from("ss"))break;return;case 2:r:{var i=a.limit-a.cursor,e=a.cursor-2;if(!(e{a.ket=a.cursor,o=a.find_among_b(n),a.bra=a.cursor;r:{var r=a.limit-a.cursor;i:{switch(o){case 1:var i=a.limit-a.cursor;e:{var e=a.limit-a.cursor;if(0==a.find_among_b(t)||a.cursor>a.limit_backward){if(a.cursor=a.limit-e,!z())break e;if(!a.slice_from("ee"))return}}a.cursor=a.limit-i;break;case 2:break i;case 3:if(0==(o=a.find_among_b(l)))break i;switch(o){case 1:var s=a.limit-a.cursor;if(!a.out_grouping_b(v,97,121))break i;if(a.cursor>a.limit_backward)break i;if(a.cursor=a.limit-s,a.bra=a.cursor,a.slice_from("ie"))break;return;case 2:if(a.cursor>a.limit_backward)break i}}break r}a.cursor=a.limit-r;var c=a.limit-a.cursor;if(!a.go_out_grouping_b(v,97,121))return;if(a.cursor--,a.cursor=a.limit-c,!a.slice_del())return;a.ket=a.cursor,a.bra=a.cursor;var o,c=a.limit-a.cursor;switch(o=a.find_among_b(f)){case 1:return a.slice_from("e");case 2:var u=a.limit-a.cursor;if(a.in_grouping_b(d,97,111)&&!(a.cursor>a.limit_backward))return;a.cursor=a.limit-u;break;case 3:return a.cursor!=h||(u=a.limit-a.cursor,q()&&(a.cursor=a.limit-u,a.slice_from("e")))}if(a.cursor=a.limit-c,a.ket=a.cursor,a.cursor<=a.limit_backward)return;if(a.cursor--,a.bra=a.cursor,!a.slice_del())return}})(),a.cursor=a.limit-r,a.limit-a.cursor),r=(a.ket=a.cursor,e=a.limit-a.cursor,(a.eq_s_b("y")||(a.cursor=a.limit-e,a.eq_s_b("Y")))&&(a.bra=a.cursor,a.out_grouping_b(v,97,121))&&a.cursor>a.limit_backward&&a.slice_from("i"),a.cursor=a.limit-i,a.limit-a.cursor),e=((()=>{var r;if(a.ket=a.cursor,0!=(r=a.find_among_b(_))&&(a.bra=a.cursor,z()))switch(r){case 1:if(a.slice_from("tion"))break;return;case 2:if(a.slice_from("ence"))break;return;case 3:if(a.slice_from("ance"))break;return;case 4:if(a.slice_from("able"))break;return;case 5:if(a.slice_from("ent"))break;return;case 6:if(a.slice_from("ize"))break;return;case 7:if(a.slice_from("ate"))break;return;case 8:if(a.slice_from("al"))break;return;case 9:if(a.slice_from("ful"))break;return;case 10:if(a.slice_from("ous"))break;return;case 11:if(a.slice_from("ive"))break;return;case 12:if(a.slice_from("ble"))break;return;case 13:if(a.slice_from("og"))break;return;case 14:if(!a.eq_s_b("l"))return;if(a.slice_from("og"))break;return;case 15:if(a.slice_from("less"))break;return;case 16:if(!a.in_grouping_b(w,99,116))return;if(a.slice_del())break}})(),a.cursor=a.limit-r,a.limit-a.cursor),i=((()=>{var r;if(a.ket=a.cursor,0!=(r=a.find_among_b(m))&&(a.bra=a.cursor,z()))switch(r){case 1:if(a.slice_from("tion"))break;return;case 2:if(a.slice_from("ate"))break;return;case 3:if(a.slice_from("al"))break;return;case 4:if(a.slice_from("ic"))break;return;case 5:if(a.slice_del())break;return;case 6:if(!Y())return;if(a.slice_del())break}})(),a.cursor=a.limit-e,a.limit-a.cursor),r=((()=>{var r;if(a.ket=a.cursor,0!=(r=a.find_among_b(b))&&(a.bra=a.cursor,Y()))switch(r){case 1:if(a.slice_del())break;return;case 2:var i=a.limit-a.cursor;if(!a.eq_s_b("s")&&(a.cursor=a.limit-i,!a.eq_s_b("t")))return;if(a.slice_del())break}})(),a.cursor=a.limit-i,a.limit-a.cursor),e=((()=>{var r;if(a.ket=a.cursor,0!=(r=a.find_among_b(k)))switch(a.bra=a.cursor,r){case 1:if(!Y()){if(!z())return;var i=a.limit-a.cursor;if(q())return;a.cursor=a.limit-i}if(a.slice_del())break;return;case 2:if(!Y())return;if(!a.eq_s_b("l"))return;if(a.slice_del())break}})(),a.cursor=a.limit-r,a.cursor=a.limit_backward,a.cursor);(()=>{if(p)for(;;){var r=a.cursor;r:{for(;;){var i=a.cursor;if(a.bra=a.cursor,a.eq_s("Y")){a.ket=a.cursor,a.cursor=i;break}if(a.cursor=i,a.cursor>=a.limit)break r;a.cursor++}if(a.slice_from("y"))continue;return}a.cursor=r;break}})(),a.cursor=e}}return!0},this.stemWord=function(r){return a.setCurrent(r),this.stem(),a.getCurrent()}}; -window.Stemmer = EnglishStemmer; diff --git a/docs_local/_build/_static/minus.png b/docs_local/_build/_static/minus.png deleted file mode 100644 index d96755fdaf8bb2214971e0db9c1fd3077d7c419d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^+#t*WBp7;*Yy1LIik>cxAr*|t7R?Mi>2?kWtu=nj kDsEF_5m^0CR;1wuP-*O&G^0G}KYk!hp00i_>zopr08q^qX#fBK diff --git a/docs_local/_build/_static/plus.png b/docs_local/_build/_static/plus.png deleted file mode 100644 index 7107cec93a979b9a5f64843235a16651d563ce2d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^+#t*WBp7;*Yy1LIik>cxAr*|t7R?Mi>2?kWtu>-2 m3q%Vub%g%s<8sJhVPMczOq}xhg9DJoz~JfX=d#Wzp$Pyb1r*Kz diff --git a/docs_local/_build/_static/pygments.css b/docs_local/_build/_static/pygments.css deleted file mode 100644 index 9392ddcb..00000000 --- a/docs_local/_build/_static/pygments.css +++ /dev/null @@ -1,84 +0,0 @@ -pre { line-height: 125%; } -td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } -span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } -td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } -span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } -.highlight .hll { background-color: #ffffcc } -.highlight { background: #f8f8f8; } -.highlight .c { color: #8F5902; font-style: italic } /* Comment */ -.highlight .err { color: #A40000; border: 1px solid #EF2929 } /* Error */ -.highlight .g { color: #000 } /* Generic */ -.highlight .k { color: #004461; font-weight: bold } /* Keyword */ -.highlight .l { color: #000 } /* Literal */ -.highlight .n { color: #000 } /* Name */ -.highlight .o { color: #582800 } /* Operator */ -.highlight .x { color: #000 } /* Other */ -.highlight .p { color: #000; font-weight: bold } /* Punctuation */ -.highlight .ch { color: #8F5902; font-style: italic } /* Comment.Hashbang */ -.highlight .cm { color: #8F5902; font-style: italic } /* Comment.Multiline */ -.highlight .cp { color: #8F5902 } /* Comment.Preproc */ -.highlight .cpf { color: #8F5902; font-style: italic } /* Comment.PreprocFile */ -.highlight .c1 { color: #8F5902; font-style: italic } /* Comment.Single */ -.highlight .cs { color: #8F5902; font-style: italic } /* Comment.Special */ -.highlight .gd { color: #A40000 } /* Generic.Deleted */ -.highlight .ge { color: #000; font-style: italic } /* Generic.Emph */ -.highlight .ges { color: #000 } /* Generic.EmphStrong */ -.highlight .gr { color: #EF2929 } /* Generic.Error */ -.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ -.highlight .gi { color: #00A000 } /* Generic.Inserted */ -.highlight .go { color: #888 } /* Generic.Output */ -.highlight .gp { color: #745334 } /* Generic.Prompt */ -.highlight .gs { color: #000; font-weight: bold } /* Generic.Strong */ -.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ -.highlight .gt { color: #A40000; font-weight: bold } /* Generic.Traceback */ -.highlight .kc { color: #004461; font-weight: bold } /* Keyword.Constant */ -.highlight .kd { color: #004461; font-weight: bold } /* Keyword.Declaration */ -.highlight .kn { color: #004461; font-weight: bold } /* Keyword.Namespace */ -.highlight .kp { color: #004461; font-weight: bold } /* Keyword.Pseudo */ -.highlight .kr { color: #004461; font-weight: bold } /* Keyword.Reserved */ -.highlight .kt { color: #004461; font-weight: bold } /* Keyword.Type */ -.highlight .ld { color: #000 } /* Literal.Date */ -.highlight .m { color: #900 } /* Literal.Number */ -.highlight .s { color: #4E9A06 } /* Literal.String */ -.highlight .na { color: #C4A000 } /* Name.Attribute */ -.highlight .nb { color: #004461 } /* Name.Builtin */ -.highlight .nc { color: #000 } /* Name.Class */ -.highlight .no { color: #000 } /* Name.Constant */ -.highlight .nd { color: #888 } /* Name.Decorator */ -.highlight .ni { color: #CE5C00 } /* Name.Entity */ -.highlight .ne { color: #C00; font-weight: bold } /* Name.Exception */ -.highlight .nf { color: #000 } /* Name.Function */ -.highlight .nl { color: #F57900 } /* Name.Label */ -.highlight .nn { color: #000 } /* Name.Namespace */ -.highlight .nx { color: #000 } /* Name.Other */ -.highlight .py { color: #000 } /* Name.Property */ -.highlight .nt { color: #004461; font-weight: bold } /* Name.Tag */ -.highlight .nv { color: #000 } /* Name.Variable */ -.highlight .ow { color: #004461; font-weight: bold } /* Operator.Word */ -.highlight .pm { color: #000; font-weight: bold } /* Punctuation.Marker */ -.highlight .w { color: #F8F8F8 } /* Text.Whitespace */ -.highlight .mb { color: #900 } /* Literal.Number.Bin */ -.highlight .mf { color: #900 } /* Literal.Number.Float */ -.highlight .mh { color: #900 } /* Literal.Number.Hex */ -.highlight .mi { color: #900 } /* Literal.Number.Integer */ -.highlight .mo { color: #900 } /* Literal.Number.Oct */ -.highlight .sa { color: #4E9A06 } /* Literal.String.Affix */ -.highlight .sb { color: #4E9A06 } /* Literal.String.Backtick */ -.highlight .sc { color: #4E9A06 } /* Literal.String.Char */ -.highlight .dl { color: #4E9A06 } /* Literal.String.Delimiter */ -.highlight .sd { color: #8F5902; font-style: italic } /* Literal.String.Doc */ -.highlight .s2 { color: #4E9A06 } /* Literal.String.Double */ -.highlight .se { color: #4E9A06 } /* Literal.String.Escape */ -.highlight .sh { color: #4E9A06 } /* Literal.String.Heredoc */ -.highlight .si { color: #4E9A06 } /* Literal.String.Interpol */ -.highlight .sx { color: #4E9A06 } /* Literal.String.Other */ -.highlight .sr { color: #4E9A06 } /* Literal.String.Regex */ -.highlight .s1 { color: #4E9A06 } /* Literal.String.Single */ -.highlight .ss { color: #4E9A06 } /* Literal.String.Symbol */ -.highlight .bp { color: #3465A4 } /* Name.Builtin.Pseudo */ -.highlight .fm { color: #000 } /* Name.Function.Magic */ -.highlight .vc { color: #000 } /* Name.Variable.Class */ -.highlight .vg { color: #000 } /* Name.Variable.Global */ -.highlight .vi { color: #000 } /* Name.Variable.Instance */ -.highlight .vm { color: #000 } /* Name.Variable.Magic */ -.highlight .il { color: #900 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/docs_local/_build/_static/searchtools.js b/docs_local/_build/_static/searchtools.js deleted file mode 100644 index e29b1c75..00000000 --- a/docs_local/_build/_static/searchtools.js +++ /dev/null @@ -1,693 +0,0 @@ -/* - * Sphinx JavaScript utilities for the full-text search. - */ -"use strict"; - -/** - * Simple result scoring code. - */ -if (typeof Scorer === "undefined") { - var Scorer = { - // Implement the following function to further tweak the score for each result - // The function takes a result array [docname, title, anchor, descr, score, filename] - // and returns the new score. - /* - score: result => { - const [docname, title, anchor, descr, score, filename, kind] = result - return score - }, - */ - - // query matches the full name of an object - objNameMatch: 11, - // or matches in the last dotted part of the object name - objPartialMatch: 6, - // Additive scores depending on the priority of the object - objPrio: { - 0: 15, // used to be importantResults - 1: 5, // used to be objectResults - 2: -5, // used to be unimportantResults - }, - // Used when the priority is not in the mapping. - objPrioDefault: 0, - - // query found in title - title: 15, - partialTitle: 7, - // query found in terms - term: 5, - partialTerm: 2, - }; -} - -// Global search result kind enum, used by themes to style search results. -// prettier-ignore -class SearchResultKind { - static get index() { return "index"; } - static get object() { return "object"; } - static get text() { return "text"; } - static get title() { return "title"; } -} - -const _removeChildren = (element) => { - while (element && element.lastChild) element.removeChild(element.lastChild); -}; - -/** - * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping - */ -const _escapeRegExp = (string) => - string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string - -const _escapeHTML = (text) => { - return text - .replaceAll("&", "&") - .replaceAll("<", "<") - .replaceAll(">", ">") - .replaceAll('"', """) - .replaceAll("'", "'"); -}; - -const _displayItem = (item, searchTerms, highlightTerms) => { - const docBuilder = DOCUMENTATION_OPTIONS.BUILDER; - const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX; - const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX; - const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY; - const contentRoot = document.documentElement.dataset.content_root; - - const [docName, title, anchor, descr, score, _filename, kind] = item; - - let listItem = document.createElement("li"); - // Add a class representing the item's type: - // can be used by a theme's CSS selector for styling - // See SearchResultKind for the class names. - listItem.classList.add(`kind-${kind}`); - let requestUrl; - let linkUrl; - if (docBuilder === "dirhtml") { - // dirhtml builder - let dirname = docName + "/"; - if (dirname.match(/\/index\/$/)) - dirname = dirname.substring(0, dirname.length - 6); - else if (dirname === "index/") dirname = ""; - requestUrl = contentRoot + dirname; - linkUrl = requestUrl; - } else { - // normal html builders - requestUrl = contentRoot + docName + docFileSuffix; - linkUrl = docName + docLinkSuffix; - } - let linkEl = listItem.appendChild(document.createElement("a")); - linkEl.href = linkUrl + anchor; - linkEl.dataset.score = score; - linkEl.innerHTML = _escapeHTML(title); - if (descr) { - listItem.appendChild(document.createElement("span")).innerHTML = - ` (${_escapeHTML(descr)})`; - // highlight search terms in the description - if (SPHINX_HIGHLIGHT_ENABLED) - // SPHINX_HIGHLIGHT_ENABLED is set in sphinx_highlight.js - highlightTerms.forEach((term) => - _highlightText(listItem, term, "highlighted"), - ); - } else if (showSearchSummary) - fetch(requestUrl) - .then((responseData) => responseData.text()) - .then((data) => { - if (data) - listItem.appendChild( - Search.makeSearchSummary(data, searchTerms, anchor), - ); - // highlight search terms in the summary - if (SPHINX_HIGHLIGHT_ENABLED) - // SPHINX_HIGHLIGHT_ENABLED is set in sphinx_highlight.js - highlightTerms.forEach((term) => - _highlightText(listItem, term, "highlighted"), - ); - }); - Search.output.appendChild(listItem); -}; -const _finishSearch = (resultCount) => { - Search.stopPulse(); - Search.title.innerText = _("Search Results"); - if (!resultCount) - Search.status.innerText = Documentation.gettext( - "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.", - ); - else - Search.status.innerText = Documentation.ngettext( - "Search finished, found one page matching the search query.", - "Search finished, found ${resultCount} pages matching the search query.", - resultCount, - ).replace("${resultCount}", resultCount); -}; -const _displayNextItem = ( - results, - resultCount, - searchTerms, - highlightTerms, -) => { - // results left, load the summary and display it - // this is intended to be dynamic (don't sub resultsCount) - if (results.length) { - _displayItem(results.pop(), searchTerms, highlightTerms); - setTimeout( - () => _displayNextItem(results, resultCount, searchTerms, highlightTerms), - 5, - ); - } - // search finished, update title and status message - else _finishSearch(resultCount); -}; -// Helper function used by query() to order search results. -// Each input is an array of [docname, title, anchor, descr, score, filename, kind]. -// Order the results by score (in opposite order of appearance, since the -// `_displayNextItem` function uses pop() to retrieve items) and then alphabetically. -const _orderResultsByScoreThenName = (a, b) => { - const leftScore = a[4]; - const rightScore = b[4]; - if (leftScore === rightScore) { - // same score: sort alphabetically - const leftTitle = a[1].toLowerCase(); - const rightTitle = b[1].toLowerCase(); - if (leftTitle === rightTitle) return 0; - return leftTitle > rightTitle ? -1 : 1; // inverted is intentional - } - return leftScore > rightScore ? 1 : -1; -}; - -/** - * Default splitQuery function. Can be overridden in ``sphinx.search`` with a - * custom function per language. - * - * The regular expression works by splitting the string on consecutive characters - * that are not Unicode letters, numbers, underscores, or emoji characters. - * This is the same as ``\W+`` in Python, preserving the surrogate pair area. - */ -if (typeof splitQuery === "undefined") { - var splitQuery = (query) => - query - .split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu) - .filter((term) => term); // remove remaining empty strings -} - -/** - * Search Module - */ -const Search = { - _index: null, - _queued_query: null, - _pulse_status: -1, - - htmlToText: (htmlString, anchor) => { - const htmlElement = new DOMParser().parseFromString( - htmlString, - "text/html", - ); - for (const removalQuery of [".headerlink", "script", "style"]) { - htmlElement.querySelectorAll(removalQuery).forEach((el) => { - el.remove(); - }); - } - if (anchor) { - const anchorContent = htmlElement.querySelector( - `[role="main"] ${anchor}`, - ); - if (anchorContent) return anchorContent.textContent; - - console.warn( - `Anchored content block not found. Sphinx search tries to obtain it via DOM query '[role=main] ${anchor}'. Check your theme or template.`, - ); - } - - // if anchor not specified or not found, fall back to main content - const docContent = htmlElement.querySelector('[role="main"]'); - if (docContent) return docContent.textContent; - - console.warn( - "Content block not found. Sphinx search tries to obtain it via DOM query '[role=main]'. Check your theme or template.", - ); - return ""; - }, - - init: () => { - const query = new URLSearchParams(window.location.search).get("q"); - document - .querySelectorAll('input[name="q"]') - .forEach((el) => (el.value = query)); - if (query) Search.performSearch(query); - }, - - loadIndex: (url) => - (document.body.appendChild(document.createElement("script")).src = url), - - setIndex: (index) => { - Search._index = index; - if (Search._queued_query !== null) { - const query = Search._queued_query; - Search._queued_query = null; - Search.query(query); - } - }, - - hasIndex: () => Search._index !== null, - - deferQuery: (query) => (Search._queued_query = query), - - stopPulse: () => (Search._pulse_status = -1), - - startPulse: () => { - if (Search._pulse_status >= 0) return; - - const pulse = () => { - Search._pulse_status = (Search._pulse_status + 1) % 4; - Search.dots.innerText = ".".repeat(Search._pulse_status); - if (Search._pulse_status >= 0) window.setTimeout(pulse, 500); - }; - pulse(); - }, - - /** - * perform a search for something (or wait until index is loaded) - */ - performSearch: (query) => { - // create the required interface elements - const searchText = document.createElement("h2"); - searchText.textContent = _("Searching"); - const searchSummary = document.createElement("p"); - searchSummary.classList.add("search-summary"); - searchSummary.innerText = ""; - const searchList = document.createElement("ul"); - searchList.setAttribute("role", "list"); - searchList.classList.add("search"); - - const out = document.getElementById("search-results"); - Search.title = out.appendChild(searchText); - Search.dots = Search.title.appendChild(document.createElement("span")); - Search.status = out.appendChild(searchSummary); - Search.output = out.appendChild(searchList); - - const searchProgress = document.getElementById("search-progress"); - // Some themes don't use the search progress node - if (searchProgress) { - searchProgress.innerText = _("Preparing search..."); - } - Search.startPulse(); - - // index already loaded, the browser was quick! - if (Search.hasIndex()) Search.query(query); - else Search.deferQuery(query); - }, - - _parseQuery: (query) => { - // stem the search terms and add them to the correct list - const stemmer = new Stemmer(); - const searchTerms = new Set(); - const excludedTerms = new Set(); - const highlightTerms = new Set(); - const objectTerms = new Set(splitQuery(query.toLowerCase().trim())); - splitQuery(query.trim()).forEach((queryTerm) => { - const queryTermLower = queryTerm.toLowerCase(); - - // maybe skip this "word" - // stopwords set is from language_data.js - if (stopwords.has(queryTermLower) || queryTerm.match(/^\d+$/)) return; - - // stem the word - let word = stemmer.stemWord(queryTermLower); - // select the correct list - if (word[0] === "-") excludedTerms.add(word.substr(1)); - else { - searchTerms.add(word); - highlightTerms.add(queryTermLower); - } - }); - - if (SPHINX_HIGHLIGHT_ENABLED) { - // SPHINX_HIGHLIGHT_ENABLED is set in sphinx_highlight.js - localStorage.setItem( - "sphinx_highlight_terms", - [...highlightTerms].join(" "), - ); - } - - // console.debug("SEARCH: searching for:"); - // console.info("required: ", [...searchTerms]); - // console.info("excluded: ", [...excludedTerms]); - - return [query, searchTerms, excludedTerms, highlightTerms, objectTerms]; - }, - - /** - * execute search (requires search index to be loaded) - */ - _performSearch: ( - query, - searchTerms, - excludedTerms, - highlightTerms, - objectTerms, - ) => { - const filenames = Search._index.filenames; - const docNames = Search._index.docnames; - const titles = Search._index.titles; - const allTitles = Search._index.alltitles; - const indexEntries = Search._index.indexentries; - - // Collect multiple result groups to be sorted separately and then ordered. - // Each is an array of [docname, title, anchor, descr, score, filename, kind]. - const normalResults = []; - const nonMainIndexResults = []; - - _removeChildren(document.getElementById("search-progress")); - - const queryLower = query.toLowerCase().trim(); - for (const [title, foundTitles] of Object.entries(allTitles)) { - if ( - title.toLowerCase().trim().includes(queryLower) - && queryLower.length >= title.length / 2 - ) { - for (const [file, id] of foundTitles) { - const score = Math.round( - (Scorer.title * queryLower.length) / title.length, - ); - const boost = titles[file] === title ? 1 : 0; // add a boost for document titles - normalResults.push([ - docNames[file], - titles[file] !== title ? `${titles[file]} > ${title}` : title, - id !== null ? "#" + id : "", - null, - score + boost, - filenames[file], - SearchResultKind.title, - ]); - } - } - } - - // search for explicit entries in index directives - for (const [entry, foundEntries] of Object.entries(indexEntries)) { - if (entry.includes(queryLower) && queryLower.length >= entry.length / 2) { - for (const [file, id, isMain] of foundEntries) { - const score = Math.round((100 * queryLower.length) / entry.length); - const result = [ - docNames[file], - titles[file], - id ? "#" + id : "", - null, - score, - filenames[file], - SearchResultKind.index, - ]; - if (isMain) { - normalResults.push(result); - } else { - nonMainIndexResults.push(result); - } - } - } - } - - // lookup as object - objectTerms.forEach((term) => - normalResults.push(...Search.performObjectSearch(term, objectTerms)), - ); - - // lookup as search terms in fulltext - normalResults.push( - ...Search.performTermsSearch(searchTerms, excludedTerms), - ); - - // let the scorer override scores with a custom scoring function - if (Scorer.score) { - normalResults.forEach((item) => (item[4] = Scorer.score(item))); - nonMainIndexResults.forEach((item) => (item[4] = Scorer.score(item))); - } - - // Sort each group of results by score and then alphabetically by name. - normalResults.sort(_orderResultsByScoreThenName); - nonMainIndexResults.sort(_orderResultsByScoreThenName); - - // Combine the result groups in (reverse) order. - // Non-main index entries are typically arbitrary cross-references, - // so display them after other results. - let results = [...nonMainIndexResults, ...normalResults]; - - // remove duplicate search results - // note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept - let seen = new Set(); - results = results.reverse().reduce((acc, result) => { - let resultStr = result - .slice(0, 4) - .concat([result[5]]) - .map((v) => String(v)) - .join(","); - if (!seen.has(resultStr)) { - acc.push(result); - seen.add(resultStr); - } - return acc; - }, []); - - return results.reverse(); - }, - - query: (query) => { - const [ - searchQuery, - searchTerms, - excludedTerms, - highlightTerms, - objectTerms, - ] = Search._parseQuery(query); - const results = Search._performSearch( - searchQuery, - searchTerms, - excludedTerms, - highlightTerms, - objectTerms, - ); - - // for debugging - //Search.lastresults = results.slice(); // a copy - // console.info("search results:", Search.lastresults); - - // print the results - _displayNextItem(results, results.length, searchTerms, highlightTerms); - }, - - /** - * search for object names - */ - performObjectSearch: (object, objectTerms) => { - const filenames = Search._index.filenames; - const docNames = Search._index.docnames; - const objects = Search._index.objects; - const objNames = Search._index.objnames; - const titles = Search._index.titles; - - const results = []; - - const objectSearchCallback = (prefix, match) => { - const name = match[4]; - const fullname = (prefix ? prefix + "." : "") + name; - const fullnameLower = fullname.toLowerCase(); - if (fullnameLower.indexOf(object) < 0) return; - - let score = 0; - const parts = fullnameLower.split("."); - - // check for different match types: exact matches of full name or - // "last name" (i.e. last dotted part) - if (fullnameLower === object || parts.slice(-1)[0] === object) - score += Scorer.objNameMatch; - else if (parts.slice(-1)[0].indexOf(object) > -1) - score += Scorer.objPartialMatch; // matches in last name - - const objName = objNames[match[1]][2]; - const title = titles[match[0]]; - - // If more than one term searched for, we require other words to be - // found in the name/title/description - const otherTerms = new Set(objectTerms); - otherTerms.delete(object); - if (otherTerms.size > 0) { - const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase(); - if ( - [...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0) - ) - return; - } - - let anchor = match[3]; - if (anchor === "") anchor = fullname; - else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname; - - const descr = objName + _(", in ") + title; - - // add custom score for some objects according to scorer - if (Scorer.objPrio.hasOwnProperty(match[2])) - score += Scorer.objPrio[match[2]]; - else score += Scorer.objPrioDefault; - - results.push([ - docNames[match[0]], - fullname, - "#" + anchor, - descr, - score, - filenames[match[0]], - SearchResultKind.object, - ]); - }; - Object.keys(objects).forEach((prefix) => - objects[prefix].forEach((array) => objectSearchCallback(prefix, array)), - ); - return results; - }, - - /** - * search for full-text terms in the index - */ - performTermsSearch: (searchTerms, excludedTerms) => { - // prepare search - const terms = Search._index.terms; - const titleTerms = Search._index.titleterms; - const filenames = Search._index.filenames; - const docNames = Search._index.docnames; - const titles = Search._index.titles; - - const scoreMap = new Map(); - const fileMap = new Map(); - - // perform the search on the required terms - searchTerms.forEach((word) => { - const files = []; - // find documents, if any, containing the query word in their text/title term indices - // use Object.hasOwnProperty to avoid mismatching against prototype properties - const arr = [ - { - files: terms.hasOwnProperty(word) ? terms[word] : undefined, - score: Scorer.term, - }, - { - files: titleTerms.hasOwnProperty(word) ? titleTerms[word] : undefined, - score: Scorer.title, - }, - ]; - // add support for partial matches - if (word.length > 2) { - const escapedWord = _escapeRegExp(word); - if (!terms.hasOwnProperty(word)) { - Object.keys(terms).forEach((term) => { - if (term.match(escapedWord)) - arr.push({ files: terms[term], score: Scorer.partialTerm }); - }); - } - if (!titleTerms.hasOwnProperty(word)) { - Object.keys(titleTerms).forEach((term) => { - if (term.match(escapedWord)) - arr.push({ files: titleTerms[term], score: Scorer.partialTitle }); - }); - } - } - - // no match but word was a required one - if (arr.every((record) => record.files === undefined)) return; - - // found search word in contents - arr.forEach((record) => { - if (record.files === undefined) return; - - let recordFiles = record.files; - if (recordFiles.length === undefined) recordFiles = [recordFiles]; - files.push(...recordFiles); - - // set score for the word in each file - recordFiles.forEach((file) => { - if (!scoreMap.has(file)) scoreMap.set(file, new Map()); - const fileScores = scoreMap.get(file); - fileScores.set(word, record.score); - }); - }); - - // create the mapping - files.forEach((file) => { - if (!fileMap.has(file)) fileMap.set(file, [word]); - else if (fileMap.get(file).indexOf(word) === -1) - fileMap.get(file).push(word); - }); - }); - - // now check if the files don't contain excluded terms - const results = []; - for (const [file, wordList] of fileMap) { - // check if all requirements are matched - - // as search terms with length < 3 are discarded - const filteredTermCount = [...searchTerms].filter( - (term) => term.length > 2, - ).length; - if ( - wordList.length !== searchTerms.size - && wordList.length !== filteredTermCount - ) - continue; - - // ensure that none of the excluded terms is in the search result - if ( - [...excludedTerms].some( - (term) => - terms[term] === file - || titleTerms[term] === file - || (terms[term] || []).includes(file) - || (titleTerms[term] || []).includes(file), - ) - ) - break; - - // select one (max) score for the file. - const score = Math.max(...wordList.map((w) => scoreMap.get(file).get(w))); - // add result to the result list - results.push([ - docNames[file], - titles[file], - "", - null, - score, - filenames[file], - SearchResultKind.text, - ]); - } - return results; - }, - - /** - * helper function to return a node containing the - * search summary for a given text. keywords is a list - * of stemmed words. - */ - makeSearchSummary: (htmlText, keywords, anchor) => { - const text = Search.htmlToText(htmlText, anchor); - if (text === "") return null; - - const textLower = text.toLowerCase(); - const actualStartPosition = [...keywords] - .map((k) => textLower.indexOf(k.toLowerCase())) - .filter((i) => i > -1) - .slice(-1)[0]; - const startWithContext = Math.max(actualStartPosition - 120, 0); - - const top = startWithContext === 0 ? "" : "..."; - const tail = startWithContext + 240 < text.length ? "..." : ""; - - let summary = document.createElement("p"); - summary.classList.add("context"); - summary.textContent = - top + text.substr(startWithContext, 240).trim() + tail; - - return summary; - }, -}; - -_ready(Search.init); diff --git a/docs_local/_build/_static/sphinx_highlight.js b/docs_local/_build/_static/sphinx_highlight.js deleted file mode 100644 index a74e103a..00000000 --- a/docs_local/_build/_static/sphinx_highlight.js +++ /dev/null @@ -1,159 +0,0 @@ -/* Highlighting utilities for Sphinx HTML documentation. */ -"use strict"; - -const SPHINX_HIGHLIGHT_ENABLED = true; - -/** - * highlight a given string on a node by wrapping it in - * span elements with the given class name. - */ -const _highlight = (node, addItems, text, className) => { - if (node.nodeType === Node.TEXT_NODE) { - const val = node.nodeValue; - const parent = node.parentNode; - const pos = val.toLowerCase().indexOf(text); - if ( - pos >= 0 - && !parent.classList.contains(className) - && !parent.classList.contains("nohighlight") - ) { - let span; - - const closestNode = parent.closest("body, svg, foreignObject"); - const isInSVG = closestNode && closestNode.matches("svg"); - if (isInSVG) { - span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); - } else { - span = document.createElement("span"); - span.classList.add(className); - } - - span.appendChild(document.createTextNode(val.substr(pos, text.length))); - const rest = document.createTextNode(val.substr(pos + text.length)); - parent.insertBefore(span, parent.insertBefore(rest, node.nextSibling)); - node.nodeValue = val.substr(0, pos); - /* There may be more occurrences of search term in this node. So call this - * function recursively on the remaining fragment. - */ - _highlight(rest, addItems, text, className); - - if (isInSVG) { - const rect = document.createElementNS( - "http://www.w3.org/2000/svg", - "rect", - ); - const bbox = parent.getBBox(); - rect.x.baseVal.value = bbox.x; - rect.y.baseVal.value = bbox.y; - rect.width.baseVal.value = bbox.width; - rect.height.baseVal.value = bbox.height; - rect.setAttribute("class", className); - addItems.push({ parent: parent, target: rect }); - } - } - } else if (node.matches && !node.matches("button, select, textarea")) { - node.childNodes.forEach((el) => _highlight(el, addItems, text, className)); - } -}; -const _highlightText = (thisNode, text, className) => { - let addItems = []; - _highlight(thisNode, addItems, text, className); - addItems.forEach((obj) => - obj.parent.insertAdjacentElement("beforebegin", obj.target), - ); -}; - -/** - * Small JavaScript module for the documentation. - */ -const SphinxHighlight = { - /** - * highlight the search words provided in localstorage in the text - */ - highlightSearchWords: () => { - if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight - - // get and clear terms from localstorage - const url = new URL(window.location); - const highlight = - localStorage.getItem("sphinx_highlight_terms") - || url.searchParams.get("highlight") - || ""; - localStorage.removeItem("sphinx_highlight_terms"); - // Update history only if '?highlight' is present; otherwise it - // clears text fragments (not set in window.location by the browser) - if (url.searchParams.has("highlight")) { - url.searchParams.delete("highlight"); - window.history.replaceState({}, "", url); - } - - // get individual terms from highlight string - const terms = highlight - .toLowerCase() - .split(/\s+/) - .filter((x) => x); - if (terms.length === 0) return; // nothing to do - - // There should never be more than one element matching "div.body" - const divBody = document.querySelectorAll("div.body"); - const body = divBody.length ? divBody[0] : document.querySelector("body"); - window.setTimeout(() => { - terms.forEach((term) => _highlightText(body, term, "highlighted")); - }, 10); - - const searchBox = document.getElementById("searchbox"); - if (searchBox === null) return; - searchBox.appendChild( - document - .createRange() - .createContextualFragment( - '

", - ), - ); - }, - - /** - * helper function to hide the search marks again - */ - hideSearchWords: () => { - document - .querySelectorAll("#searchbox .highlight-link") - .forEach((el) => el.remove()); - document - .querySelectorAll("span.highlighted") - .forEach((el) => el.classList.remove("highlighted")); - localStorage.removeItem("sphinx_highlight_terms"); - }, - - initEscapeListener: () => { - // only install a listener if it is really needed - if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return; - - document.addEventListener("keydown", (event) => { - // bail for input elements - if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) - return; - // bail with special keys - if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) - return; - if ( - DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS - && event.key === "Escape" - ) { - SphinxHighlight.hideSearchWords(); - event.preventDefault(); - } - }); - }, -}; - -_ready(() => { - /* Do not call highlightSearchWords() when we are on the search page. - * It will highlight words from the *previous* search query. - */ - if (typeof Search === "undefined") SphinxHighlight.highlightSearchWords(); - SphinxHighlight.initEscapeListener(); -}); diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/claude_skill/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/claude_skill/index.html deleted file mode 100644 index 46d28d74..00000000 --- a/docs_local/_build/autoapi/PowerPlatform/Dataverse/claude_skill/index.html +++ /dev/null @@ -1,122 +0,0 @@ - - - - - - - - PowerPlatform.Dataverse.claude_skill — PowerPlatform-Dataverse-Client 0.1.0b11 documentation - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

PowerPlatform.Dataverse.claude_skill

-

Claude Code skill package for the PowerPlatform Dataverse Client SDK.

-

This package contains two skills: -- dataverse-sdk-use: Guidance for using the SDK in your applications -- dataverse-sdk-dev: Guidance for developing/contributing to the SDK itself

-
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/client/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/client/index.html deleted file mode 100644 index 130299b2..00000000 --- a/docs_local/_build/autoapi/PowerPlatform/Dataverse/client/index.html +++ /dev/null @@ -1,295 +0,0 @@ - - - - - - - - PowerPlatform.Dataverse.client — PowerPlatform-Dataverse-Client 0.1.0b11 documentation - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

PowerPlatform.Dataverse.client

-
-

Classes

- - - - - - -

DataverseClient

High-level client for Microsoft Dataverse operations.

-
-
-

Module Contents

-
-
-class PowerPlatform.Dataverse.client.DataverseClient(base_url: str, credential: azure.core.credentials.TokenCredential, config: PowerPlatform.Dataverse.core.config.DataverseConfig | None = None, *, context: PowerPlatform.Dataverse.core.config.OperationContext | None = None)
-

High-level client for Microsoft Dataverse operations.

-

This client provides a simple, stable interface for interacting with Dataverse environments -through the Web API. It handles authentication via Azure Identity and delegates HTTP operations -to an internal OData client.

-
-
Key capabilities:
    -
  • OData CRUD operations: create, read, update, delete records

  • -
  • SQL queries: execute read-only SQL via Web API ?sql parameter

  • -
  • Table metadata: create, inspect, and delete custom tables; create and delete columns

  • -
  • File uploads: upload files to file columns with chunking support

  • -
-
-
-
-
Parameters:
-
    -
  • base_url (str) – Your Dataverse environment URL, for example -"https://org.crm.dynamics.com". Trailing slash is automatically removed.

  • -
  • credential (TokenCredential) – Azure Identity credential for authentication.

  • -
  • config (DataverseConfig or None) – Optional configuration for language, timeouts, and retries. -If not provided, defaults are loaded from from_env().

  • -
  • context (OperationContext or None) – Optional caller-defined context object appended to the -outbound User-Agent header for plugin/tool attribution. Cannot be used -together with config – pass the context via -DataverseConfig instead.

  • -
-
-
Raises:
-
    -
  • ValueError – If base_url is missing or empty after trimming.

  • -
  • ValueError – If both config and context are provided.

  • -
-
-
-
-

Note

-

The client lazily initializes its internal OData client on first use, allowing lightweight construction without immediate network calls.

-
-
-

Note

-

All methods that communicate with the Dataverse Web API may raise -HttpError on non-successful -HTTP responses (e.g. 401, 403, 404, 429, 500). Individual method -docstrings document only domain-specific exceptions.

-
-

Operations are organized into namespaces:

-
    -
  • client.records – create, update, delete, and get records (single or paginated queries)

  • -
  • client.query – query and search operations

  • -
  • client.tables – table and column metadata management

  • -
  • client.files – file upload operations

  • -
  • client.dataframe – pandas DataFrame wrappers for record CRUD

  • -
  • client.batch – batch multiple operations into a single HTTP request

  • -
-

The client supports Python’s context manager protocol for automatic resource -cleanup and HTTP connection pooling:

-

Example

-

Recommended – context manager (enables HTTP connection pooling):

-
from azure.identity import InteractiveBrowserCredential
-from PowerPlatform.Dataverse.client import DataverseClient
-
-credential = InteractiveBrowserCredential()
-
-with DataverseClient("https://org.crm.dynamics.com", credential) as client:
-    record_id = client.records.create("account", {"name": "Contoso Ltd"})
-    client.records.update("account", record_id, {"telephone1": "555-0100"})
-# Session closed, caches cleared automatically
-
-
-

Manual lifecycle:

-
client = DataverseClient("https://org.crm.dynamics.com", credential)
-try:
-    record_id = client.records.create("account", {"name": "Contoso Ltd"})
-finally:
-    client.close()
-
-
-
-
-auth
-
- -
-
-records
-
- -
-
-query
-
- -
-
-tables
-
- -
-
-files
-
- -
-
-dataframe
-
- -
-
-batch
-
- -
-
-close() None
-

Close the client and release resources.

-

Closes the HTTP session (if any), clears internal caches, and -marks the client as closed. Safe to call multiple times. After -closing, any operation will raise RuntimeError.

-

Called automatically when using the client as a context manager.

-

Example:

-
client = DataverseClient(base_url, credential)
-try:
-    client.records.create("account", {"name": "Contoso"})
-finally:
-    client.close()
-
-
-
- -
-
-flush_cache(kind) int
-

Flush cached client metadata or state.

-
-
Parameters:
-

kind (str) –

Cache kind to flush. Currently supported values:

-
    -
  • "picklist": Clears picklist label cache used for label-to-integer conversion

  • -
-

Future kinds (e.g. "entityset", "primaryid") may be added without -breaking this signature.

-

-
-
Returns:
-

Number of cache entries removed.

-
-
Return type:
-

int

-
-
-

Example

-

Clear the picklist cache:

-
removed = client.flush_cache("picklist")
-print(f"Cleared {removed} cached picklist entries")
-
-
-
- -
- -
-
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/common/constants/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/common/constants/index.html deleted file mode 100644 index 2d696a88..00000000 --- a/docs_local/_build/autoapi/PowerPlatform/Dataverse/common/constants/index.html +++ /dev/null @@ -1,209 +0,0 @@ - - - - - - - - PowerPlatform.Dataverse.common.constants — PowerPlatform-Dataverse-Client 0.1.0b11 documentation - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

PowerPlatform.Dataverse.common.constants

-

Constants for Dataverse Web API metadata types.

-

These constants define the OData type identifiers used in Web API payloads -for metadata operations.

-
-

Attributes

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

ODATA_TYPE_LOCALIZED_LABEL

ODATA_TYPE_LABEL

ODATA_TYPE_LOOKUP_ATTRIBUTE

ODATA_TYPE_ONE_TO_MANY_RELATIONSHIP

ODATA_TYPE_MANY_TO_MANY_RELATIONSHIP

CASCADE_BEHAVIOR_CASCADE

Perform the action on all referencing table records associated with the referenced table record.

CASCADE_BEHAVIOR_NO_CASCADE

Do not apply the action to any referencing table records associated with the referenced table record.

CASCADE_BEHAVIOR_REMOVE_LINK

Remove the value of the referencing column for all referencing table records when the referenced record is deleted.

CASCADE_BEHAVIOR_RESTRICT

Prevent the referenced table record from being deleted when referencing table records exist.

-
-
-

Module Contents

-
-
-PowerPlatform.Dataverse.common.constants.ODATA_TYPE_LOCALIZED_LABEL = 'Microsoft.Dynamics.CRM.LocalizedLabel'
-
- -
-
-PowerPlatform.Dataverse.common.constants.ODATA_TYPE_LABEL = 'Microsoft.Dynamics.CRM.Label'
-
- -
-
-PowerPlatform.Dataverse.common.constants.ODATA_TYPE_LOOKUP_ATTRIBUTE = 'Microsoft.Dynamics.CRM.LookupAttributeMetadata'
-
- -
-
-PowerPlatform.Dataverse.common.constants.ODATA_TYPE_ONE_TO_MANY_RELATIONSHIP = 'Microsoft.Dynamics.CRM.OneToManyRelationshipMetadata'
-
- -
-
-PowerPlatform.Dataverse.common.constants.ODATA_TYPE_MANY_TO_MANY_RELATIONSHIP = 'Microsoft.Dynamics.CRM.ManyToManyRelationshipMetadata'
-
- -
-
-PowerPlatform.Dataverse.common.constants.CASCADE_BEHAVIOR_CASCADE = 'Cascade'
-

Perform the action on all referencing table records associated with the referenced table record.

-
- -
-
-PowerPlatform.Dataverse.common.constants.CASCADE_BEHAVIOR_NO_CASCADE = 'NoCascade'
-

Do not apply the action to any referencing table records associated with the referenced table record.

-
- -
- -

Remove the value of the referencing column for all referencing table records when the referenced record is deleted.

-
- -
-
-PowerPlatform.Dataverse.common.constants.CASCADE_BEHAVIOR_RESTRICT = 'Restrict'
-

Prevent the referenced table record from being deleted when referencing table records exist.

-
- -
-
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/common/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/common/index.html deleted file mode 100644 index 276be10e..00000000 --- a/docs_local/_build/autoapi/PowerPlatform/Dataverse/common/index.html +++ /dev/null @@ -1,128 +0,0 @@ - - - - - - - - PowerPlatform.Dataverse.common — PowerPlatform-Dataverse-Client 0.1.0b11 documentation - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

PowerPlatform.Dataverse.common

-

Common utilities and constants for the Dataverse SDK.

-

This module contains shared constants and utilities used across the SDK.

-
-

Submodules

- -
-
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/core/config/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/core/config/index.html deleted file mode 100644 index cce886b3..00000000 --- a/docs_local/_build/autoapi/PowerPlatform/Dataverse/core/config/index.html +++ /dev/null @@ -1,231 +0,0 @@ - - - - - - - - PowerPlatform.Dataverse.core.config — PowerPlatform-Dataverse-Client 0.1.0b11 documentation - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

PowerPlatform.Dataverse.core.config

-

Dataverse client configuration.

-

Provides DataverseConfig, a lightweight -immutable container for locale and (reserved) HTTP tuning options plus the -convenience constructor from_env().

-
-

Classes

- - - - - - - - - -

OperationContext

Caller-defined context appended to outbound User-Agent headers.

DataverseConfig

Configuration settings for Dataverse client operations.

-
-
-

Module Contents

-
-
-class PowerPlatform.Dataverse.core.config.OperationContext
-

Caller-defined context appended to outbound User-Agent headers.

-

The context string is validated to be semicolon-separated key=value pairs -using only allowed keys (app, skill, agent) with values from -closed allowlists. Free-form text, email addresses, PII, and unknown keys -are rejected.

-
-
Parameters:
-

user_agent_context (str) – Attribution string in key=value;key=value format.

-
-
Raises:
-

ValueError – If the string is empty, contains control characters, -does not match the required key=value format, or uses unknown -keys/values.

-
-
-
-
-user_agent_context: str
-
- -
- -
-
-class PowerPlatform.Dataverse.core.config.DataverseConfig
-

Configuration settings for Dataverse client operations.

-
-
Parameters:
-
    -
  • language_code (int) – LCID (Locale ID) for localized labels and messages. Default is 1033 (English - United States).

  • -
  • http_retries (int or None) – Optional maximum number of retry attempts for transient HTTP errors. Reserved for future use.

  • -
  • http_backoff (float or None) – Optional backoff multiplier (in seconds) between retry attempts. Reserved for future use.

  • -
  • http_timeout (float or None) – Optional request timeout in seconds. Reserved for future use.

  • -
  • log_config (LogConfig or None) – Optional local HTTP diagnostics logging configuration. -When provided, all HTTP requests and responses are logged to timestamped -.log files with automatic redaction of sensitive headers.

  • -
  • operation_context (OperationContext or None) – Optional caller-defined context object appended to the -outbound User-Agent header as a parenthesized comment. Intended for -plugin/tool attribution.

  • -
-
-
-
-
-language_code: int = 1033
-
- -
-
-http_retries: int | None = None
-
- -
-
-http_backoff: float | None = None
-
- -
-
-http_timeout: float | None = None
-
- -
-
-log_config: PowerPlatform.Dataverse.core.log_config.LogConfig | None = None
-
- -
-
-operation_context: OperationContext | None = None
-
- -
-
-classmethod from_env() DataverseConfig
-

Create a configuration instance with default settings.

-
-
Returns:
-

Configuration instance with default values.

-
-
Return type:
-

DataverseConfig

-
-
-
- -
- -
-
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/core/errors/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/core/errors/index.html deleted file mode 100644 index a6fb74c9..00000000 --- a/docs_local/_build/autoapi/PowerPlatform/Dataverse/core/errors/index.html +++ /dev/null @@ -1,304 +0,0 @@ - - - - - - - - PowerPlatform.Dataverse.core.errors — PowerPlatform-Dataverse-Client 0.1.0b11 documentation - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

PowerPlatform.Dataverse.core.errors

-

Structured Dataverse exception hierarchy.

-

This module provides DataverseError and -specialized ValidationError, -MetadataError, -SQLParseError, and -HttpError for validation, metadata, -SQL parsing, and Web API HTTP failures.

-
-

Exceptions

- - - - - - - - - - - - - - - - - - -

DataverseError

Base structured exception for the Dataverse SDK.

ValidationError

Exception raised for client-side validation failures.

MetadataError

Exception raised for metadata operation failures.

SQLParseError

Exception raised for SQL query parsing failures.

HttpError

Exception raised for HTTP request failures from the Dataverse Web API.

-
-
-

Module Contents

-
-
-exception PowerPlatform.Dataverse.core.errors.DataverseError(message: str, code: str, subcode: str | None = None, status_code: int | None = None, details: Dict[str, Any] | None = None, source: str | None = None, is_transient: bool = False)
-

Bases: Exception

-

Base structured exception for the Dataverse SDK.

-
-
Parameters:
-
    -
  • message (str) – Human-readable error message.

  • -
  • code (str) – Error category code (e.g. "validation_error", "http_error").

  • -
  • subcode (str | None) – Optional subcategory or specific error identifier.

  • -
  • status_code (int | None) – Optional HTTP status code if the error originated from an HTTP response.

  • -
  • details (dict | None) – Optional dictionary containing additional diagnostic information.

  • -
  • source (str) – Error source, either "client" or "server".

  • -
  • is_transient (bool) – Whether the error is potentially transient and may succeed on retry.

  • -
-
-
-

Initialize self. See help(type(self)) for accurate signature.

-
-
-message
-
- -
-
-code
-
- -
-
-subcode = None
-
- -
-
-status_code = None
-
- -
-
-details
-
- -
-
-source = 'client'
-
- -
-
-is_transient = False
-
- -
-
-timestamp
-
- -
-
-to_dict() Dict[str, Any]
-

Convert the error to a dictionary representation.

-
-
Returns:
-

Dictionary containing all error properties.

-
-
Return type:
-

dict

-
-
-
- -
- -
-
-exception PowerPlatform.Dataverse.core.errors.ValidationError(message: str, *, subcode: str | None = None, details: Dict[str, Any] | None = None)
-

Bases: DataverseError

-

Exception raised for client-side validation failures.

-
-
Parameters:
-
    -
  • message (str) – Human-readable validation error message.

  • -
  • subcode (str | None) – Optional specific validation error identifier.

  • -
  • details (dict | None) – Optional dictionary with additional validation context.

  • -
-
-
-

Initialize self. See help(type(self)) for accurate signature.

-
- -
-
-exception PowerPlatform.Dataverse.core.errors.MetadataError(message: str, *, subcode: str | None = None, details: Dict[str, Any] | None = None)
-

Bases: DataverseError

-

Exception raised for metadata operation failures.

-
-
Parameters:
-
    -
  • message (str) – Human-readable metadata error message.

  • -
  • subcode (str | None) – Optional specific metadata error identifier.

  • -
  • details (dict | None) – Optional dictionary with additional metadata context.

  • -
-
-
-

Initialize self. See help(type(self)) for accurate signature.

-
- -
-
-exception PowerPlatform.Dataverse.core.errors.SQLParseError(message: str, *, subcode: str | None = None, details: Dict[str, Any] | None = None)
-

Bases: DataverseError

-

Exception raised for SQL query parsing failures.

-
-
Parameters:
-
    -
  • message (str) – Human-readable SQL parsing error message.

  • -
  • subcode (str | None) – Optional specific SQL parsing error identifier.

  • -
  • details (dict | None) – Optional dictionary with SQL query context and parse information.

  • -
-
-
-

Initialize self. See help(type(self)) for accurate signature.

-
- -
-
-exception PowerPlatform.Dataverse.core.errors.HttpError(message: str, status_code: int, is_transient: bool = False, subcode: str | None = None, service_error_code: str | None = None, correlation_id: str | None = None, client_request_id: str | None = None, service_request_id: str | None = None, traceparent: str | None = None, body_excerpt: str | None = None, retry_after: int | None = None, details: Dict[str, Any] | None = None)
-

Bases: DataverseError

-

Exception raised for HTTP request failures from the Dataverse Web API.

-
-
Parameters:
-
    -
  • message (str) – Human-readable HTTP error message, typically from the API error response.

  • -
  • status_code (int) – HTTP status code (e.g. 400, 404, 500).

  • -
  • is_transient (bool) – Whether the error is transient (429, 503, 504) and may succeed on retry.

  • -
  • subcode (str | None) – Optional HTTP status category (e.g. "4xx", "5xx").

  • -
  • service_error_code (str | None) – Optional Dataverse-specific error code from the API response.

  • -
  • correlation_id (str | None) – Optional client-generated correlation ID for tracking requests within an SDK call.

  • -
  • client_request_id (str | None) – Optional client-generated request ID injected into outbound headers.

  • -
  • service_request_id (str | None) – Optional x-ms-service-request-id value returned by Dataverse servers.

  • -
  • traceparent (str | None) – Optional W3C trace context for distributed tracing.

  • -
  • body_excerpt (str | None) – Optional excerpt of the response body for diagnostics.

  • -
  • retry_after (int | None) – Optional number of seconds to wait before retrying (from Retry-After header).

  • -
  • details (dict | None) – Optional additional diagnostic details.

  • -
-
-
-

Initialize self. See help(type(self)) for accurate signature.

-
- -
-
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/core/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/core/index.html deleted file mode 100644 index bd0cc295..00000000 --- a/docs_local/_build/autoapi/PowerPlatform/Dataverse/core/index.html +++ /dev/null @@ -1,131 +0,0 @@ - - - - - - - - PowerPlatform.Dataverse.core — PowerPlatform-Dataverse-Client 0.1.0b11 documentation - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

PowerPlatform.Dataverse.core

-

Core infrastructure components for the Dataverse SDK.

-

This module contains the foundational components including authentication, -configuration, HTTP client, and error handling.

-
-

Submodules

- -
-
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/core/log_config/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/core/log_config/index.html deleted file mode 100644 index 0e2c8348..00000000 --- a/docs_local/_build/autoapi/PowerPlatform/Dataverse/core/log_config/index.html +++ /dev/null @@ -1,201 +0,0 @@ - - - - - - - - PowerPlatform.Dataverse.core.log_config — PowerPlatform-Dataverse-Client 0.1.0b11 documentation - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

PowerPlatform.Dataverse.core.log_config

-

Local file logging configuration for Dataverse SDK HTTP diagnostics.

-

Provides LogConfig, an opt-in configuration for writing request/response -traces to .log files with automatic header redaction and timestamped filenames.

-
-

Classes

- - - - - - -

LogConfig

Configuration for local HTTP diagnostics logging.

-
-
-

Module Contents

-
-
-class PowerPlatform.Dataverse.core.log_config.LogConfig
-

Configuration for local HTTP diagnostics logging.

-

When provided to DataverseClient via -DataverseConfig, every HTTP request -and response is logged to timestamped .log files in the specified folder. -Sensitive headers (e.g. Authorization) are automatically redacted.

-
-
Parameters:
-
    -
  • log_folder – Directory path for log files. Created automatically if missing. -Default: "./dataverse_logs"

  • -
  • log_file_prefix – Filename prefix. Timestamp is appended automatically. -Default: "dataverse"dataverse_20260310_143022.log

  • -
  • max_body_bytes – Maximum bytes of request/response body to capture. -0 (default) disables body capture. Enable only for active debugging -sessions — bodies may contain PII and sensitive business data.

  • -
  • redacted_headers – Header names (case-insensitive) whose values are -replaced with "[REDACTED]" in logs. Defaults include -Authorization, Proxy-Authorization, etc.

  • -
  • log_level – Python logging level name. Default: "DEBUG".

  • -
  • max_file_bytes – Max size per log file before rotation (bytes). -Default: 10_485_760 (10 MB).

  • -
  • backup_count – Number of rotated backup files to keep. Default: 5.

  • -
-
-
-
-
-log_folder: str = './dataverse_logs'
-
- -
-
-log_file_prefix: str = 'dataverse'
-
- -
-
-max_body_bytes: int = 0
-
- -
-
-redacted_headers: FrozenSet[str]
-
- -
-
-log_level: str = 'DEBUG'
-
- -
-
-max_file_bytes: int = 10485760
-
- -
-
-backup_count: int = 5
-
- -
- -
-
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/data/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/data/index.html deleted file mode 100644 index 645e7d97..00000000 --- a/docs_local/_build/autoapi/PowerPlatform/Dataverse/data/index.html +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - - - PowerPlatform.Dataverse.data — PowerPlatform-Dataverse-Client 0.1.0b11 documentation - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

PowerPlatform.Dataverse.data

-

Data access layer for the Dataverse SDK.

-

This module contains OData protocol handling, CRUD operations, metadata management, -SQL query functionality, and file upload capabilities.

-
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/extensions/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/extensions/index.html deleted file mode 100644 index f703a817..00000000 --- a/docs_local/_build/autoapi/PowerPlatform/Dataverse/extensions/index.html +++ /dev/null @@ -1,119 +0,0 @@ - - - - - - - - PowerPlatform.Dataverse.extensions — PowerPlatform-Dataverse-Client 0.1.0b11 documentation - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

PowerPlatform.Dataverse.extensions

-

Optional extensions for the Dataverse SDK. Currently a placeholder.

-
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/index.html deleted file mode 100644 index 3ed02f56..00000000 --- a/docs_local/_build/autoapi/PowerPlatform/Dataverse/index.html +++ /dev/null @@ -1,133 +0,0 @@ - - - - - - - - PowerPlatform.Dataverse — PowerPlatform-Dataverse-Client 0.1.0b11 documentation - - - - - - - - - - - - - - - - - - - - -
- - -
-
- - - - - - - \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/migration/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/migration/index.html deleted file mode 100644 index 80968581..00000000 --- a/docs_local/_build/autoapi/PowerPlatform/Dataverse/migration/index.html +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - PowerPlatform.Dataverse.migration — PowerPlatform-Dataverse-Client 0.1.0b11 documentation - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

PowerPlatform.Dataverse.migration

-
-

Submodules

- -
-
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index.html deleted file mode 100644 index 136066a2..00000000 --- a/docs_local/_build/autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index.html +++ /dev/null @@ -1,247 +0,0 @@ - - - - - - - - PowerPlatform.Dataverse.migration.migrate_v0_to_v1 — PowerPlatform-Dataverse-Client 0.1.0b11 documentation - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

PowerPlatform.Dataverse.migration.migrate_v0_to_v1

-

DV-Python-SDK v0 -> v1 GA migration codemod.

-

Mechanically rewrites beta (0.1.0b*) call sites to their GA (1.0) equivalents -using LibCST (concrete syntax tree — preserves all whitespace and comments).

-

Usage:

-
pip install PowerPlatform-Dataverse-Client[migration]
-dataverse-migrate path/to/your/scripts/
-dataverse-migrate path/to/your/scripts/ --dry-run          # preview without writing
-dataverse-migrate path/to/your/scripts/ --client-var=svc   # if client is named 'svc'
-
-# Or via module for development installs:
-python -m PowerPlatform.Dataverse.migration.migrate_v0_to_v1 path/to/your/scripts/
-
-
-
-

Transformations applied

-

Builder methods (.filter_* -> .where(col(…)…)):

-
.filter_eq("col", v)               ->  .where(col("col") == v)
-.filter_ne("col", v)               ->  .where(col("col") != v)
-.filter_gt("col", v)               ->  .where(col("col") > v)
-.filter_ge("col", v)               ->  .where(col("col") >= v)
-.filter_lt("col", v)               ->  .where(col("col") < v)
-.filter_le("col", v)               ->  .where(col("col") <= v)
-.filter_contains("col", v)         ->  .where(col("col").contains(v))
-.filter_startswith("col", v)       ->  .where(col("col").startswith(v))
-.filter_endswith("col", v)         ->  .where(col("col").endswith(v))
-.filter_in("col", vals)            ->  .where(col("col").in_(vals))
-.filter_not_in("col", vals)        ->  .where(col("col").not_in(vals))
-.filter_null("col")                ->  .where(col("col").is_null())
-.filter_not_null("col")            ->  .where(col("col").is_not_null())
-.filter_between("col", lo, hi)     ->  .where(col("col").between(lo, hi))
-.filter_not_between("col", lo, hi) ->  .where(col("col").not_between(lo, hi))
-.filter_raw("expr")                ->  .where(raw("expr"))
-.filter("expr")                    ->  .where(raw("expr"))
-.execute(by_page=True)             ->  .execute_pages()
-.execute(by_page=False)            ->  .execute()  (flag removed)
-<builder_chain>.to_dataframe()     ->  <builder_chain>.execute().to_dataframe()
-    Inserts .execute() when the receiver is a recognised QueryBuilder chain
-    (contains .builder(), .select(), .where(), or a .filter_*() call).
-
-
-

Record namespace:

-
batch.records.get(t, id)     ->  batch.records.retrieve(t, id)
-
-
-

Top-level shortcuts (removed at GA):

-
client.create(t, d)           ->  client.records.create(t, d)
-client.update(t, id, d)       ->  client.records.update(t, id, d)
-client.delete(t, id)          ->  client.records.delete(t, id)
-client.get(t, id)             ->  client.records.get(t, id)  [deprecated; see manual section]
-client.query_sql(sql)         ->  client.query.sql(sql)
-client.get_table_info(t)      ->  client.tables.get(t)
-client.create_table(t, …)     ->  client.tables.create(t, …)
-client.delete_table(t)        ->  client.tables.delete(t)
-client.list_tables()          ->  client.tables.list()
-client.create_columns(t, …)   ->  client.tables.add_columns(t, …)
-client.delete_columns(t, …)   ->  client.tables.remove_columns(t, …)
-client.upload_file(…)         ->  client.files.upload(…)
-
-
-
-
Import management:

Adds from PowerPlatform.Dataverse.models.filters import col when a -.filter_* method is rewritten (if col is not already imported). -Adds raw to the same import when .filter_raw or .filter is rewritten.

-
-
NOT handled by this codemod (manual migration required):

execute(by_page=variable) -> manual review required (variable argument, not literal) -client.records.get(t, id) -> client.records.retrieve(t, id)

-
-

Return type changes: beta returns Record (raises on 404); GA retrieve() returns -Record | None. Callers that do not guard against None will fail silently.

-
-
-
client.records.get(t, kw=…) -> client.records.list(t, kw=…)

Return type changes: beta returns Iterable[List[Record]] (pages); GA list() -returns QueryResult (flat iterable over Records). Any for page in result: -for rec in page: iteration pattern breaks after a mechanical rename.

-
-
client.dataframe.get() -> client.query.builder(…).execute().to_dataframe()

Expression reconstruction requires understanding caller intent.

-
-
-

client.query.sql_select()/sql_join()/sql_joins() -> removed (no mechanical replacement)

-
-
-
-
-

Functions

- - - - - - - - - - - - - - - -

find_manual_patterns(→ List[str])

Return descriptions of patterns in source that require manual migration.

migrate_source(→ str)

Parse source, apply transformations, return migrated source.

migrate_file(→ Tuple[bool, List[str]])

Migrate path in place. Returns (was_changed, manual_review_notes).

main(→ int)

-
-
-

Module Contents

-
-
-PowerPlatform.Dataverse.migration.migrate_v0_to_v1.find_manual_patterns(source: str, *, client_var: str = 'client') List[str]
-

Return descriptions of patterns in source that require manual migration.

-
- -
-
-PowerPlatform.Dataverse.migration.migrate_v0_to_v1.migrate_source(source: str, *, client_var: str = 'client') str
-

Parse source, apply transformations, return migrated source.

-
- -
-
-PowerPlatform.Dataverse.migration.migrate_v0_to_v1.migrate_file(path: pathlib.Path, *, dry_run: bool = False, client_var: str = 'client') Tuple[bool, List[str]]
-

Migrate path in place. Returns (was_changed, manual_review_notes).

-
- -
-
-PowerPlatform.Dataverse.migration.migrate_v0_to_v1.main(argv: List[str] | None = None) int
-
- -
-
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/batch/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/batch/index.html deleted file mode 100644 index 1acd508c..00000000 --- a/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/batch/index.html +++ /dev/null @@ -1,267 +0,0 @@ - - - - - - - - PowerPlatform.Dataverse.models.batch — PowerPlatform-Dataverse-Client 0.1.0b11 documentation - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

PowerPlatform.Dataverse.models.batch

-

Public result types for batch operations.

-
-

Classes

- - - - - - - - - -

BatchItemResponse

Response from a single operation within a batch request.

BatchResult

Result of executing a batch request.

-
-
-

Module Contents

-
-
-class PowerPlatform.Dataverse.models.batch.BatchItemResponse
-

Response from a single operation within a batch request.

-

Responses are returned in submission order. For operations added to a -changeset, responses appear in the changeset’s position in that order.

-
-
Parameters:
-
    -
  • status_code – HTTP status code for this operation (e.g. 204, 200, 400).

  • -
  • content_idContent-ID value from the changeset response part, if any.

  • -
  • entity_id – GUID extracted from the OData-EntityId response header. -Set for successful create (POST) operations.

  • -
  • data – Parsed JSON response body (e.g. for GET operations).

  • -
  • error_message – Error message when the operation failed.

  • -
  • error_code – Service error code when the operation failed.

  • -
-
-
-

Example:

-
for item in result.responses:
-    if item.is_success:
-        print(f"[OK] {item.status_code} entity_id={item.entity_id}")
-    else:
-        print(f"[ERR] {item.status_code}: {item.error_message}")
-
-
-
-
-status_code: int
-
- -
-
-content_id: str | None = None
-
- -
-
-entity_id: str | None = None
-
- -
-
-data: Dict[str, Any] | None = None
-
- -
-
-error_message: str | None = None
-
- -
-
-error_code: str | None = None
-
- -
-
-property is_success: bool
-

Return True when status_code is 2xx.

-
- -
- -
-
-class PowerPlatform.Dataverse.models.batch.BatchResult
-

Result of executing a batch request.

-

Contains one BatchItemResponse per HTTP operation submitted. -Operations that expand to multiple HTTP requests (e.g. add_columns -with three columns) contribute three entries.

-
-
Parameters:
-

responses – All responses in submission order.

-
-
-

Example:

-
result = client.batch.new().execute()
-print(f"Succeeded: {len(result.succeeded)}, Failed: {len(result.failed)}")
-for guid in result.entity_ids:
-    print(f"[OK] entity_id: {guid}")
-
-
-
-
-responses: List[BatchItemResponse] = []
-
- -
-
-property succeeded: List[BatchItemResponse]
-

Responses with 2xx status codes.

-
- -
-
-property failed: List[BatchItemResponse]
-

Responses with non-2xx status codes.

-
- -
-
-property has_errors: bool
-

True when any response has a non-2xx status code.

-
- -
-
-property entity_ids: List[str]
-

GUIDs extracted from OData-EntityId headers of successful responses.

-

Returns entity IDs from any successful (2xx) response that includes an -OData-EntityId header. Both individual POST (create) and -PATCH (update) operations return this header with the record’s GUID. -GET and DELETE operations do not.

-
-

Note

-

CreateMultiple and UpsertMultiple action responses do not -return per-record OData-EntityId headers. Their IDs are in the -JSON response body (data["Ids"]). Access them via:

-
for resp in result.succeeded:
-    if resp.data and "Ids" in resp.data:
-        bulk_ids = resp.data["Ids"]
-
-
-
-
- -
- -
-
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index.html deleted file mode 100644 index 2e3ae64b..00000000 --- a/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index.html +++ /dev/null @@ -1,201 +0,0 @@ - - - - - - - - PowerPlatform.Dataverse.models.fetchxml_query — PowerPlatform-Dataverse-Client 0.1.0b11 documentation - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

PowerPlatform.Dataverse.models.fetchxml_query

-

FetchXmlQuery — inert query object returned by QueryOperations.fetchxml().

-
-

Classes

- - - - - - -

FetchXmlQuery

Inert FetchXML query object. No HTTP request is made until

-
-
-

Module Contents

-
-
-class PowerPlatform.Dataverse.models.fetchxml_query.FetchXmlQuery(xml: str, entity_name: str, client: PowerPlatform.Dataverse.client.DataverseClient)
-

Inert FetchXML query object. No HTTP request is made until -execute() or execute_pages() is called.

-

Obtained via client.query.fetchxml(xml).

-
-
Parameters:
-
    -
  • xml – Stripped, well-formed FetchXML string.

  • -
  • entity_name – Entity schema name from the <entity> element.

  • -
  • client – Parent DataverseClient.

  • -
-
-
-
-
-execute() PowerPlatform.Dataverse.models.record.QueryResult
-

Execute the FetchXML query and return all results as a QueryResult.

-

Blocking — fetches all pages upfront and holds every record in memory before -returning. Simple for small-to-medium result sets; use execute_pages() -when the result set may be large or you want to process records as they arrive.

-
-
Returns:
-

All matching records across all pages.

-
-
Return type:
-

QueryResult

-
-
-

Example:

-
rows = client.query.fetchxml(xml).execute()
-df = rows.to_dataframe()
-
-
-
- -
-
-execute_pages() Iterator[PowerPlatform.Dataverse.models.record.QueryResult]
-

Lazily yield one QueryResult per HTTP page.

-

Streaming — each iteration fires one HTTP request and yields one page. -Prefer over execute() when:

-
    -
  • The result set may be large and you do not want all records in memory at once.

  • -
  • You want early exit: stop iterating once you find what you need and the -remaining HTTP round-trips are skipped automatically.

  • -
  • You need per-page progress reporting or batched downstream writes.

  • -
-

One-shot — do not iterate more than once.

-
-
Returns:
-

Iterator of per-page QueryResult objects.

-
-
Return type:
-

Iterator[QueryResult]

-
-
-

Example:

-
for page in client.query.fetchxml(xml).execute_pages():
-    process(page.to_dataframe())
-
-
-
- -
- -
-
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/filters/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/filters/index.html deleted file mode 100644 index a1666b0a..00000000 --- a/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/filters/index.html +++ /dev/null @@ -1,550 +0,0 @@ - - - - - - - - PowerPlatform.Dataverse.models.filters — PowerPlatform-Dataverse-Client 0.1.0b11 documentation - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

PowerPlatform.Dataverse.models.filters

-

Composable OData filter expressions for the Dataverse SDK.

-

Provides an expression tree that compiles to OData $filter strings, -with Python operator overloads (&, |, ~) for composing -complex filter conditions.

-

Example:

-
from PowerPlatform.Dataverse.models.filters import col, raw
-
-# Preferred GA idiom — col() proxy
-expr = col("statecode") == 0
-print(expr.to_odata())  # statecode eq 0
-
-# Complex composition with OR and AND
-expr = (col("statecode") == 0) | (col("statecode") == 1) & (col("revenue") > 100000)
-print(expr.to_odata())
-
-# In / not-in
-expr = col("statecode").in_([0, 1, 2])
-print(expr.to_odata())
-# Microsoft.Dynamics.CRM.In(PropertyName='statecode',PropertyValues=["0","1","2"])
-
-# Raw OData escape hatch (no deprecation warning)
-expr = raw("Microsoft.Dynamics.CRM.Today(PropertyName='createdon')")
-
-# Negation
-expr = ~(col("statecode") == 1)
-print(expr.to_odata())  # not (statecode eq 1)
-
-
-
-

Classes

- - - - - - - - - -

FilterExpression

Base class for composable OData filter expressions.

ColumnProxy

Fluent proxy for building OData filter expressions from a column name.

-
-
-

Functions

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

col(→ ColumnProxy)

Return a ColumnProxy for building filter expressions.

raw(→ FilterExpression)

Verbatim OData filter expression (passed through unchanged).

eq(→ FilterExpression)

Equality filter: column eq value.

ne(→ FilterExpression)

Not-equal filter: column ne value.

gt(→ FilterExpression)

Greater-than filter: column gt value.

ge(→ FilterExpression)

Greater-than-or-equal filter: column ge value.

lt(→ FilterExpression)

Less-than filter: column lt value.

le(→ FilterExpression)

Less-than-or-equal filter: column le value.

contains(→ FilterExpression)

Contains filter: contains(column, value).

startswith(→ FilterExpression)

Startswith filter: startswith(column, value).

endswith(→ FilterExpression)

Endswith filter: endswith(column, value).

between(→ FilterExpression)

Between filter: (column ge low and column le high).

is_null(→ FilterExpression)

Null check: column eq null.

is_not_null(→ FilterExpression)

Not-null check: column ne null.

filter_in(→ FilterExpression)

In filter using Microsoft.Dynamics.CRM.In.

not_in(→ FilterExpression)

Not-in filter using Microsoft.Dynamics.CRM.NotIn.

not_between(→ FilterExpression)

Not-between filter: not (column ge low and column le high).

-
-
-

Module Contents

-
-
-class PowerPlatform.Dataverse.models.filters.FilterExpression
-

Base class for composable OData filter expressions.

-

Supports Python operator overloads for logical composition:

-
    -
  • expr1 & expr2 produces (expr1 and expr2)

  • -
  • expr1 | expr2 produces (expr1 or expr2)

  • -
  • ~expr produces not (expr)

  • -
-
-
-abstractmethod to_odata() str
-

Compile this expression to an OData $filter string.

-
- -
- -
-
-class PowerPlatform.Dataverse.models.filters.ColumnProxy(name: str)
-

Fluent proxy for building OData filter expressions from a column name.

-

Returned by col(). Operator overloads and methods produce -FilterExpression instances that can be passed to -QueryBuilder.where().

-

Example:

-
from PowerPlatform.Dataverse.models.filters import col
-
-expr = col("statecode") == 0               # equality
-expr = col("revenue") > 1_000_000          # comparison
-expr = col("name").like("Contoso%")        # startswith
-expr = col("name").is_null()               # null check
-expr = col("statecode").in_([0, 1])        # in
-
-
-
-
-is_null() FilterExpression
-

Column equals null: column eq null.

-
- -
-
-is_not_null() FilterExpression
-

Column not null: column ne null.

-
- -
-
-in_(values: Collection[Any]) FilterExpression
-

In filter using Microsoft.Dynamics.CRM.In.

-
-
Parameters:
-

values – Non-empty collection of values.

-
-
Raises:
-

ValueError – If values is empty.

-
-
-
- -
-
-not_in(values: Collection[Any]) FilterExpression
-

Not-in filter using Microsoft.Dynamics.CRM.NotIn.

-
-
Parameters:
-

values – Non-empty collection of values.

-
-
Raises:
-

ValueError – If values is empty.

-
-
-
- -
-
-between(lo: Any, hi: Any) FilterExpression
-

Between filter: (column ge lo and column le hi).

-
- -
-
-not_between(lo: Any, hi: Any) FilterExpression
-

Not-between filter: not (column ge lo and column le hi).

-
- -
-
-contains(value: str) FilterExpression
-

Contains filter: contains(column, value).

-
- -
-
-startswith(value: str) FilterExpression
-

Startswith filter: startswith(column, value).

-
- -
-
-endswith(value: str) FilterExpression
-

Endswith filter: endswith(column, value).

-
- -
-
-like(pattern: str) FilterExpression
-

Pattern-match filter compiled to the closest OData equivalent.

-
-
Parameters:
-

pattern – LIKE-style pattern string.

-
-
Raises:
-

ValueError – If the pattern cannot be reduced to a single OData function.

-
-
-
- -
-
-not_like(pattern: str) FilterExpression
-

Negated pattern-match filter; mirrors like() rules then negates.

-
-
Parameters:
-

pattern – LIKE-style pattern string (same rules as like()).

-
-
Raises:
-

ValueError – If the pattern cannot be reduced to a single OData function.

-
-
-
- -
- -
-
-PowerPlatform.Dataverse.models.filters.col(name: str) ColumnProxy
-

Return a ColumnProxy for building filter expressions.

-

This is the preferred GA idiom for constructing filter expressions:

-
from PowerPlatform.Dataverse.models.filters import col
-
-expr = col("statecode") == 0
-expr = col("revenue") > 1_000_000
-expr = col("name").like("Contoso%")
-expr = col("statecode").in_([0, 1])
-expr = col("parentaccountid").is_null()
-
-
-
-
Parameters:
-

name – Column logical name (case-insensitive, will be lowercased).

-
-
Returns:
-

A ColumnProxy bound to the column.

-
-
Raises:
-

ValueError – If name is empty.

-
-
-
- -
-
-PowerPlatform.Dataverse.models.filters.raw(filter_string: str) FilterExpression
-

Verbatim OData filter expression (passed through unchanged).

-

This function is not deprecated — it is the OData escape hatch with -no typed replacement.

-
-
Parameters:
-

filter_string – Raw OData filter string.

-
-
Returns:
-

A FilterExpression.

-
-
-

Example:

-
raw("Microsoft.Dynamics.CRM.Today(PropertyName='createdon')")
-
-
-
- -
-
-PowerPlatform.Dataverse.models.filters.eq(column: str, value: Any) FilterExpression
-

Equality filter: column eq value.

-
-

Deprecated since version Use: col(column) == value instead.

-
-
- -
-
-PowerPlatform.Dataverse.models.filters.ne(column: str, value: Any) FilterExpression
-

Not-equal filter: column ne value.

-
-

Deprecated since version Use: col(column) != value instead.

-
-
- -
-
-PowerPlatform.Dataverse.models.filters.gt(column: str, value: Any) FilterExpression
-

Greater-than filter: column gt value.

-
-

Deprecated since version Use: col(column) > value instead.

-
-
- -
-
-PowerPlatform.Dataverse.models.filters.ge(column: str, value: Any) FilterExpression
-

Greater-than-or-equal filter: column ge value.

-
-

Deprecated since version Use: col(column) >= value instead.

-
-
- -
-
-PowerPlatform.Dataverse.models.filters.lt(column: str, value: Any) FilterExpression
-

Less-than filter: column lt value.

-
-

Deprecated since version Use: col(column) < value instead.

-
-
- -
-
-PowerPlatform.Dataverse.models.filters.le(column: str, value: Any) FilterExpression
-

Less-than-or-equal filter: column le value.

-
-

Deprecated since version Use: col(column) <= value instead.

-
-
- -
-
-PowerPlatform.Dataverse.models.filters.contains(column: str, value: str) FilterExpression
-

Contains filter: contains(column, value).

-
-

Deprecated since version Use: col(column).contains(value) instead.

-
-
- -
-
-PowerPlatform.Dataverse.models.filters.startswith(column: str, value: str) FilterExpression
-

Startswith filter: startswith(column, value).

-
-

Deprecated since version Use: col(column).startswith(value) instead.

-
-
- -
-
-PowerPlatform.Dataverse.models.filters.endswith(column: str, value: str) FilterExpression
-

Endswith filter: endswith(column, value).

-
-

Deprecated since version Use: col(column).endswith(value) instead.

-
-
- -
-
-PowerPlatform.Dataverse.models.filters.between(column: str, low: Any, high: Any) FilterExpression
-

Between filter: (column ge low and column le high).

-
-

Deprecated since version Use: col(column).between(low, high) instead.

-
-
- -
-
-PowerPlatform.Dataverse.models.filters.is_null(column: str) FilterExpression
-

Null check: column eq null.

-
-

Deprecated since version Use: col(column).is_null() instead.

-
-
- -
-
-PowerPlatform.Dataverse.models.filters.is_not_null(column: str) FilterExpression
-

Not-null check: column ne null.

-
-

Deprecated since version Use: col(column).is_not_null() instead.

-
-
- -
-
-PowerPlatform.Dataverse.models.filters.filter_in(column: str, values: Collection[Any]) FilterExpression
-

In filter using Microsoft.Dynamics.CRM.In.

-

Named filter_in because in is a Python keyword.

-
-

Deprecated since version Use: col(column).in_(values) instead.

-
-
-
Raises:
-

ValueError – If values is empty.

-
-
-
- -
-
-PowerPlatform.Dataverse.models.filters.not_in(column: str, values: Collection[Any]) FilterExpression
-

Not-in filter using Microsoft.Dynamics.CRM.NotIn.

-
-

Deprecated since version Use: col(column).not_in(values) instead.

-
-
-
Raises:
-

ValueError – If values is empty.

-
-
-
- -
-
-PowerPlatform.Dataverse.models.filters.not_between(column: str, low: Any, high: Any) FilterExpression
-

Not-between filter: not (column ge low and column le high).

-
-

Deprecated since version Use: col(column).not_between(low, high) instead.

-
-
- -
-
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/index.html deleted file mode 100644 index 9f080066..00000000 --- a/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/index.html +++ /dev/null @@ -1,148 +0,0 @@ - - - - - - - - PowerPlatform.Dataverse.models — PowerPlatform-Dataverse-Client 0.1.0b11 documentation - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

PowerPlatform.Dataverse.models

-

Data models and type definitions for the Dataverse SDK.

-

Provides dataclasses and helpers for Dataverse entities:

- -
-

Submodules

- -
-
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/labels/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/labels/index.html deleted file mode 100644 index 5d028e87..00000000 --- a/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/labels/index.html +++ /dev/null @@ -1,233 +0,0 @@ - - - - - - - - PowerPlatform.Dataverse.models.labels — PowerPlatform-Dataverse-Client 0.1.0b11 documentation - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

PowerPlatform.Dataverse.models.labels

-

Label models for Dataverse metadata.

-
-

Classes

- - - - - - - - - -

LocalizedLabel

Represents a localized label with a language code.

Label

Represents a label that can have multiple localized versions.

-
-
-

Module Contents

-
-
-class PowerPlatform.Dataverse.models.labels.LocalizedLabel
-

Represents a localized label with a language code.

-
-
Parameters:
-
    -
  • label (str) – The text of the label.

  • -
  • language_code (int) – The language code (LCID), e.g., 1033 for English.

  • -
  • additional_properties (Optional[Dict[str, Any]]) – Optional dict of additional properties to include -in the Web API payload. These are merged last and can override default values.

  • -
-
-
-
-
-label: str
-
- -
-
-language_code: int
-
- -
-
-additional_properties: Dict[str, Any] | None = None
-
- -
-
-to_dict() Dict[str, Any]
-

Convert to Web API JSON format.

-

Example:

-
>>> label = LocalizedLabel(label="Account", language_code=1033)
->>> label.to_dict()
-{
-    '@odata.type': 'Microsoft.Dynamics.CRM.LocalizedLabel',
-    'Label': 'Account',
-    'LanguageCode': 1033
-}
-
-
-
- -
- -
-
-class PowerPlatform.Dataverse.models.labels.Label
-

Represents a label that can have multiple localized versions.

-
-
Parameters:
-
    -
  • localized_labels (List[LocalizedLabel]) – List of LocalizedLabel instances.

  • -
  • user_localized_label (Optional[LocalizedLabel]) – Optional user-specific localized label.

  • -
  • additional_properties (Optional[Dict[str, Any]]) – Optional dict of additional properties to include -in the Web API payload. These are merged last and can override default values.

  • -
-
-
-
-
-localized_labels: List[LocalizedLabel]
-
- -
-
-user_localized_label: LocalizedLabel | None = None
-
- -
-
-additional_properties: Dict[str, Any] | None = None
-
- -
-
-to_dict() Dict[str, Any]
-

Convert to Web API JSON format.

-

Example:

-
>>> label = Label(localized_labels=[LocalizedLabel("Account", 1033)])
->>> label.to_dict()
-{
-    '@odata.type': 'Microsoft.Dynamics.CRM.Label',
-    'LocalizedLabels': [
-        {'@odata.type': '...', 'Label': 'Account', 'LanguageCode': 1033}
-    ],
-    'UserLocalizedLabel': {'@odata.type': '...', 'Label': 'Account', ...}
-}
-
-
-
- -
- -
-
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/protocol/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/protocol/index.html deleted file mode 100644 index 2112eb2d..00000000 --- a/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/protocol/index.html +++ /dev/null @@ -1,202 +0,0 @@ - - - - - - - - PowerPlatform.Dataverse.models.protocol — PowerPlatform-Dataverse-Client 0.1.0b11 documentation - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

PowerPlatform.Dataverse.models.protocol

-

DataverseModel structural Protocol for typed entity integration.

-
-

Classes

- - - - - - -

DataverseModel

Structural Protocol enabling typed entity instances to be passed to

-
-
-

Module Contents

-
-
-class PowerPlatform.Dataverse.models.protocol.DataverseModel
-

Bases: Protocol

-

Structural Protocol enabling typed entity instances to be passed to -records.create() and records.update().

-

Implement this Protocol on any entity class (dataclass, Pydantic model, -hand-rolled) to enable it to be passed directly to CRUD operations without -specifying the table name or converting to dict manually.

-

Required class variables:

-
    -
  • __entity_logical_name__ — Dataverse logical entity name (e.g. "account")

  • -
  • __entity_set_name__ — OData entity set name (e.g. "accounts")

  • -
-

Required instance methods:

-
    -
  • to_dict() — return record payload as dict

  • -
  • from_dict(data) — classmethod to reconstruct from a response dict

  • -
-

Example:

-
from dataclasses import dataclass
-from PowerPlatform.Dataverse import DataverseModel
-
-@dataclass
-class Account:
-    __entity_logical_name__ = "account"
-    __entity_set_name__ = "accounts"
-    name: str = ""
-    telephone1: str = ""
-
-    def to_dict(self) -> dict:
-        return {"name": self.name, "telephone1": self.telephone1}
-
-    @classmethod
-    def from_dict(cls, data: dict) -> "Account":
-        return cls(
-            name=data.get("name", ""),
-            telephone1=data.get("telephone1", ""),
-        )
-
-# isinstance() works today — Protocol is runtime_checkable:
-assert isinstance(Account(), DataverseModel)
-
-# Type your own helpers against the Protocol now:
-def save(entity: DataverseModel) -> None:
-    data = entity.to_dict()
-    client.records.create(entity.__entity_logical_name__, data)
-
-
-
-

Note

-

Direct dispatch (client.records.create(entity) without a table name -or dict) is not yet supported and will be added in a future release.

-
-
-
-to_dict() dict
-

Return the record payload as a plain dictionary.

-
- -
-
-classmethod from_dict(data: dict) DataverseModel
-

Reconstruct an instance from a response dictionary.

-
- -
- -
-
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/query_builder/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/query_builder/index.html deleted file mode 100644 index 7bdfe0f8..00000000 --- a/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/query_builder/index.html +++ /dev/null @@ -1,504 +0,0 @@ - - - - - - - - PowerPlatform.Dataverse.models.query_builder — PowerPlatform-Dataverse-Client 0.1.0b11 documentation - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

PowerPlatform.Dataverse.models.query_builder

-

Fluent query builder for constructing OData queries.

-

Provides a type-safe, discoverable interface for building complex queries -against Dataverse tables with method chaining.

-

Example:

-
# Via client (recommended) -- flat iteration over records
-from PowerPlatform.Dataverse.models import col
-
-for record in (client.query.builder("account")
-               .select("name", "revenue")
-               .where(col("statecode") == 0)
-               .where(col("revenue") > 1_000_000)
-               .order_by("revenue", descending=True)
-               .top(100)
-               .execute()):
-    print(record["name"])
-
-# With composable expression tree
-from PowerPlatform.Dataverse.models import col, raw
-
-for record in (client.query.builder("account")
-               .select("name", "revenue")
-               .where((col("statecode") == 0) | (col("statecode") == 1))
-               .where(col("revenue") > 100000)
-               .top(100)
-               .execute()):
-    print(record["name"])
-
-# Lazy paged iteration (one QueryResult per HTTP page)
-for page in (client.query.builder("account")
-             .select("name")
-             .execute_pages()):
-    process_batch(page)
-
-# Get results as a pandas DataFrame
-df = (client.query.builder("account")
-      .select("name", "telephone1")
-      .where(col("statecode") == 0)
-      .top(100)
-      .execute()
-      .to_dataframe())
-
-
-
-

Classes

- - - - - - - - - - - - -

QueryParams

Typed dictionary returned by QueryBuilder.build().

ExpandOption

Structured options for an $expand navigation property.

QueryBuilder

Fluent interface for building and executing OData queries against a sync client.

-
-
-

Module Contents

-
-
-class PowerPlatform.Dataverse.models.query_builder.QueryParams
-

Bases: TypedDict

-

Typed dictionary returned by QueryBuilder.build().

-

Provides IDE autocomplete when passing build results to -client.records.list() manually.

-

Initialize self. See help(type(self)) for accurate signature.

-
-
-table: str
-
- -
-
-select: List[str]
-
- -
-
-filter: str
-
- -
-
-orderby: List[str]
-
- -
-
-expand: List[str]
-
- -
-
-top: int
-
- -
-
-page_size: int
-
- -
-
-count: bool
-
- -
-
-include_annotations: str
-
- -
- -
-
-class PowerPlatform.Dataverse.models.query_builder.ExpandOption(relation: str)
-

Structured options for an $expand navigation property.

-

Allows specifying nested $select, $filter, $orderby, and -$top options for a single navigation property expansion, following -the OData $expand syntax.

-
-
Parameters:
-

relation (str) – Navigation property name (case-sensitive).

-
-
-

Example:

-
# Expand Account_Tasks with nested options
-opt = (ExpandOption("Account_Tasks")
-       .select("subject", "createdon")
-       .filter("contains(subject,'Task')")
-       .order_by("createdon", descending=True)
-       .top(5))
-
-query = (client.query.builder("account")
-         .select("name")
-         .expand(opt)
-         .execute())
-
-
-
-
-relation
-
- -
-
-select(*columns: str) ExpandOption
-

Select specific columns from the expanded entity.

-
-
Parameters:
-

columns – Column names to select.

-
-
Returns:
-

Self for method chaining.

-
-
-
- -
-
-filter(filter_str: str) ExpandOption
-

Filter the expanded collection.

-
-
Parameters:
-

filter_str – OData $filter expression.

-
-
Returns:
-

Self for method chaining.

-
-
-
- -
-
-order_by(column: str, descending: bool = False) ExpandOption
-

Sort the expanded collection.

-
-
Parameters:
-
    -
  • column – Column name to sort by.

  • -
  • descending – Sort descending if True.

  • -
-
-
Returns:
-

Self for method chaining.

-
-
-
- -
-
-top(count: int) ExpandOption
-

Limit expanded results.

-
-
Parameters:
-

count – Maximum number of expanded records.

-
-
Returns:
-

Self for method chaining.

-
-
-
- -
-
-to_odata() str
-

Compile to OData $expand syntax.

-
-
Returns:
-

OData expand string like "Nav($select=col1,col2;$filter=...)"

-
-
Return type:
-

str

-
-
-
- -
- -
-
-class PowerPlatform.Dataverse.models.query_builder.QueryBuilder(table: str)
-

Bases: _QueryBuilderBase

-

Fluent interface for building and executing OData queries against a sync client.

-

Provides method chaining for constructing complex queries with -composable filter expressions. Can be used standalone (via build()) -or bound to a client (via execute()).

-
-
Parameters:
-

table (str) – Table schema name to query.

-
-
Raises:
-

ValueError – If table is empty.

-
-
-

Example

-

Standalone query construction:

-
from PowerPlatform.Dataverse.models import col
-
-query = (QueryBuilder("account")
-         .select("name")
-         .where(col("statecode") == 0)
-         .top(10))
-params = query.build()
-# {"table": "account", "select": ["name"],
-#  "filter": "statecode eq 0", "top": 10}
-
-
-
-
-execute(*, by_page=_BY_PAGE_UNSET) PowerPlatform.Dataverse.models.record.QueryResult | Iterator[PowerPlatform.Dataverse.models.record.QueryResult]
-

Execute the query and return results.

-

Returns a QueryResult -with all pages collected. Use execute_pages() for lazy per-page -iteration.

-

This method is only available when the QueryBuilder was created -via client.query.builder(table). Standalone QueryBuilder -instances should use build() to get parameters and pass them -to client.records.list() manually.

-

At least one of select(), where(), or top() must be -called before execute(); otherwise a ValueError is -raised to prevent accidental full-table scans.

-
-

Deprecated since version The: by_page parameter is deprecated. Use execute_pages() -for lazy per-page iteration, or plain execute() (no flag) for -the default eager result.

-
-
-
Returns:
-

QueryResult -with all pages collected (default), or page iterator (deprecated -by_page=True).

-
-
Return type:
-

QueryResult or Iterator[QueryResult]

-
-
Raises:
-
    -
  • ValueError – If no select, where, or top -constraint has been set.

  • -
  • RuntimeError – If the query was not created via -client.query.builder().

  • -
-
-
-

Example:

-
from PowerPlatform.Dataverse.models import col
-
-for record in (client.query.builder("account")
-               .select("name")
-               .where(col("statecode") == 0)
-               .execute()):
-    print(record["name"])
-
-
-
- -
-
-execute_pages() Iterator[PowerPlatform.Dataverse.models.record.QueryResult]
-

Lazily yield one QueryResult -per HTTP page.

-

Each iteration triggers a network request via @odata.nextLink. -One-shot — do not iterate more than once.

-

At least one of select(), where(), or top() must be -called before execute_pages(); otherwise a ValueError is -raised to prevent accidental full-table scans.

-
-
Returns:
-

Iterator of per-page QueryResult.

-
-
Return type:
-

Iterator[QueryResult]

-
-
Raises:
-
    -
  • ValueError – If no select, where, or top -constraint has been set.

  • -
  • RuntimeError – If the query was not created via -client.query.builder().

  • -
-
-
-

Example:

-
from PowerPlatform.Dataverse.models import col
-
-for page in (client.query.builder("account")
-             .select("name")
-             .where(col("statecode") == 0)
-             .execute_pages()):
-    process(page.to_dataframe())
-
-
-
- -
-
-to_dataframe() pandas.DataFrame
-

Execute the query and return results as a pandas DataFrame.

-
-

Deprecated since version Use: QueryBuilder.execute().to_dataframe() instead. -QueryBuilder.to_dataframe() will be removed in a future release.

-
-

All pages are consolidated into a single DataFrame.

-

This method is only available when the QueryBuilder was created -via client.query.builder(table).

-

At least one of select(), where(), or top() must be -called before to_dataframe(); otherwise a ValueError -is raised to prevent accidental full-table scans.

-
-
Returns:
-

DataFrame containing all matching records. Returns an empty -DataFrame when no records match.

-
-
Return type:
-

DataFrame

-
-
Raises:
-
    -
  • ValueError – If no select, where, or top -constraint has been set.

  • -
  • RuntimeError – If the query was not created via -client.query.builder().

  • -
-
-
-

Example:

-
from PowerPlatform.Dataverse.models import col
-
-df = (client.query.builder("account")
-      .select("name", "telephone1")
-      .where(col("statecode") == 0)
-      .top(100)
-      .execute()
-      .to_dataframe())
-
-
-
- -
- -
-
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/record/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/record/index.html deleted file mode 100644 index 1474cd36..00000000 --- a/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/record/index.html +++ /dev/null @@ -1,273 +0,0 @@ - - - - - - - - PowerPlatform.Dataverse.models.record — PowerPlatform-Dataverse-Client 0.1.0b11 documentation - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

PowerPlatform.Dataverse.models.record

-

Record data model for Dataverse entities.

-
-

Classes

- - - - - - - - - -

Record

Strongly-typed Dataverse record with dict-like backward compatibility.

QueryResult

Iterable wrapper around a list of Record objects.

-
-
-

Module Contents

-
-
-class PowerPlatform.Dataverse.models.record.Record
-

Strongly-typed Dataverse record with dict-like backward compatibility.

-

Wraps raw OData response data into a structured object while preserving -result["key"] access patterns for existing code.

-
-
Parameters:
-
    -
  • id (str) – Record GUID. Empty string if not extracted (e.g. paginated -results, SQL queries).

  • -
  • table (str) – Table schema name used in the request.

  • -
  • data (dict) – Record field data as key-value pairs.

  • -
  • etag (str or None) – ETag for optimistic concurrency, extracted from -@odata.etag in the API response.

  • -
-
-
-

Example:

-
record = client.records.get("account", account_id, select=["name"])
-print(record.id)          # structured access
-print(record["name"])     # dict-like access (backward compat)
-
-
-
-
-id: str = ''
-
- -
-
-table: str = ''
-
- -
-
-data: Dict[str, Any]
-
- -
-
-etag: str | None = None
-
- -
-
-get(key: str, default: Any = None) Any
-

Return value for key, or default if not present.

-
- -
-
-keys() KeysView[str]
-

Return data keys.

-
- -
-
-values() ValuesView[Any]
-

Return data values.

-
- -
-
-items() ItemsView[str, Any]
-

Return data items.

-
- -
-
-classmethod from_api_response(table: str, response_data: Dict[str, Any], *, record_id: str = '') Record
-

Create a Record from a raw OData API response.

-

Strips @odata.* annotation keys from the data and extracts the -@odata.etag value if present.

-
-
Parameters:
-
    -
  • table (str) – Table schema name.

  • -
  • response_data (dict) – Raw JSON dict from the OData response.

  • -
  • record_id (str) – Known record GUID. Pass explicitly when available -(e.g. single-record get). Defaults to empty string.

  • -
-
-
Return type:
-

Record

-
-
-
- -
-
-to_dict() Dict[str, Any]
-

Return a plain dict copy of the record data (excludes metadata).

-
- -
- -
-
-class PowerPlatform.Dataverse.models.record.QueryResult(records: List[Record])
-

Iterable wrapper around a list of Record objects.

-

Returned by execute() -(flat mode) and list().

-

Backward-compatible: for r in result continues to work without change.

-
-
Parameters:
-

records (list[Record]) – Collected records from all pages.

-
-
-
-
-records: List[Record]
-
- -
-
-first() Record | None
-

Return the first record, or None if the result is empty.

-
- -
-
-to_dataframe() Any
-

Return all records as a pandas DataFrame.

-
-
Raises:
-

ImportError – If pandas is not installed.

-
-
Return type:
-

DataFrame

-
-
-
- -
- -
-
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/relationship/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/relationship/index.html deleted file mode 100644 index efe93ab4..00000000 --- a/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/relationship/index.html +++ /dev/null @@ -1,586 +0,0 @@ - - - - - - - - PowerPlatform.Dataverse.models.relationship — PowerPlatform-Dataverse-Client 0.1.0b11 documentation - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

PowerPlatform.Dataverse.models.relationship

-

Relationship models for Dataverse (input and output).

-
-

Classes

- - - - - - - - - - - - - - - - - - -

CascadeConfiguration

Defines cascade behavior for relationship operations.

LookupAttributeMetadata

Metadata for a lookup attribute.

OneToManyRelationshipMetadata

Metadata for a one-to-many entity relationship.

ManyToManyRelationshipMetadata

Metadata for a many-to-many entity relationship.

RelationshipInfo

Typed return model for relationship metadata.

-
-
-

Module Contents

-
-
-class PowerPlatform.Dataverse.models.relationship.CascadeConfiguration
-

Defines cascade behavior for relationship operations.

-
-
Parameters:
-
    -
  • assign (str) – Cascade behavior for assign operations.

  • -
  • delete (str) – Cascade behavior for delete operations.

  • -
  • merge (str) – Cascade behavior for merge operations.

  • -
  • reparent (str) – Cascade behavior for reparent operations.

  • -
  • share (str) – Cascade behavior for share operations.

  • -
  • unshare (str) – Cascade behavior for unshare operations.

  • -
  • additional_properties (Optional[Dict[str, Any]]) – Optional dict of additional properties to include -in the Web API payload (e.g., “Archive”, “RollupView”). These are merged -last and can override default values.

  • -
-
-
-
-
Valid values for each parameter:
    -
  • “Cascade”: Perform the operation on all related records

  • -
  • “NoCascade”: Do not perform the operation on related records

  • -
  • “RemoveLink”: Remove the relationship link but keep the records

  • -
  • “Restrict”: Prevent the operation if related records exist

  • -
-
-
-
-
-assign: str = 'NoCascade'
-
- -
-
-delete: str = 'RemoveLink'
-
- -
-
-merge: str = 'NoCascade'
-
- -
-
-reparent: str = 'NoCascade'
-
- -
-
-share: str = 'NoCascade'
-
- -
-
-unshare: str = 'NoCascade'
-
- -
-
-additional_properties: Dict[str, Any] | None = None
-
- -
-
-to_dict() Dict[str, Any]
-

Convert to Web API JSON format.

-

Example:

-
>>> config = CascadeConfiguration(delete="Cascade", assign="NoCascade")
->>> config.to_dict()
-{
-    'Assign': 'NoCascade',
-    'Delete': 'Cascade',
-    'Merge': 'NoCascade',
-    'Reparent': 'NoCascade',
-    'Share': 'NoCascade',
-    'Unshare': 'NoCascade'
-}
-
-
-
- -
- -
-
-class PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata
-

Metadata for a lookup attribute.

-
-
Parameters:
-
    -
  • schema_name (str) – Schema name for the attribute (e.g., “new_AccountId”).

  • -
  • display_name (Label) – Display name for the attribute.

  • -
  • description (Optional[Label]) – Optional description of the attribute.

  • -
  • required_level (str) – Requirement level for the attribute.

  • -
  • additional_properties (Optional[Dict[str, Any]]) – Optional dict of additional properties to include -in the Web API payload. Useful for setting properties like “Targets” (to -specify which entity types the lookup can reference), “LogicalName”, -“IsSecured”, “IsValidForAdvancedFind”, etc. These are merged last and -can override default values.

  • -
-
-
-
-
Valid required_level values:
    -
  • “None”: The attribute is optional

  • -
  • “Recommended”: The attribute is recommended

  • -
  • “ApplicationRequired”: The attribute is required

  • -
-
-
-
-
-schema_name: str
-
- -
-
-display_name: PowerPlatform.Dataverse.models.labels.Label
-
- -
-
-description: PowerPlatform.Dataverse.models.labels.Label | None = None
-
- -
-
-required_level: str = 'None'
-
- -
-
-additional_properties: Dict[str, Any] | None = None
-
- -
-
-to_dict() Dict[str, Any]
-

Convert to Web API JSON format.

-

Example:

-
>>> lookup = LookupAttributeMetadata(
-...     schema_name="new_AccountId",
-...     display_name=Label([LocalizedLabel("Account", 1033)])
-... )
->>> lookup.to_dict()
-{
-    '@odata.type': 'Microsoft.Dynamics.CRM.LookupAttributeMetadata',
-    'SchemaName': 'new_AccountId',
-    'AttributeType': 'Lookup',
-    'AttributeTypeName': {'Value': 'LookupType'},
-    'DisplayName': {...},
-    'RequiredLevel': {'Value': 'None', 'CanBeChanged': True, ...}
-}
-
-
-
- -
- -
-
-class PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata
-

Metadata for a one-to-many entity relationship.

-
-
Parameters:
-
    -
  • schema_name (str) – Schema name for the relationship (e.g., “new_Account_Orders”).

  • -
  • referenced_entity (str) – Logical name of the referenced (parent) entity.

  • -
  • referencing_entity (str) – Logical name of the referencing (child) entity.

  • -
  • referenced_attribute (str) – Attribute on the referenced entity (typically the primary key).

  • -
  • cascade_configuration (CascadeConfiguration) – Cascade behavior configuration.

  • -
  • referencing_attribute (Optional[str]) – Optional name for the referencing attribute (usually auto-generated).

  • -
  • additional_properties (Optional[Dict[str, Any]]) – Optional dict of additional properties to include -in the Web API payload. Useful for setting inherited properties like -“IsValidForAdvancedFind”, “IsCustomizable”, “SecurityTypes”, etc. -These are merged last and can override default values.

  • -
-
-
-
-
-schema_name: str
-
- -
-
-referenced_entity: str
-
- -
-
-referencing_entity: str
-
- -
-
-referenced_attribute: str
-
- -
-
-cascade_configuration: CascadeConfiguration
-
- -
-
-referencing_attribute: str | None = None
-
- -
-
-additional_properties: Dict[str, Any] | None = None
-
- -
-
-to_dict() Dict[str, Any]
-

Convert to Web API JSON format.

-

Example:

-
>>> rel = OneToManyRelationshipMetadata(
-...     schema_name="new_account_orders",
-...     referenced_entity="account",
-...     referencing_entity="new_order",
-...     referenced_attribute="accountid"
-... )
->>> rel.to_dict()
-{
-    '@odata.type': 'Microsoft.Dynamics.CRM.OneToManyRelationshipMetadata',
-    'SchemaName': 'new_account_orders',
-    'ReferencedEntity': 'account',
-    'ReferencingEntity': 'new_order',
-    'ReferencedAttribute': 'accountid',
-    'CascadeConfiguration': {...}
-}
-
-
-
- -
- -
-
-class PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata
-

Metadata for a many-to-many entity relationship.

-
-
Parameters:
-
    -
  • schema_name (str) – Schema name for the relationship.

  • -
  • entity1_logical_name (str) – Logical name of the first entity.

  • -
  • entity2_logical_name (str) – Logical name of the second entity.

  • -
  • intersect_entity_name (Optional[str]) – Name for the intersect table (defaults to schema_name if not provided).

  • -
  • additional_properties (Optional[Dict[str, Any]]) – Optional dict of additional properties to include -in the Web API payload. Useful for setting inherited properties like -“IsValidForAdvancedFind”, “IsCustomizable”, “SecurityTypes”, or direct -properties like “Entity1NavigationPropertyName”. These are merged last -and can override default values.

  • -
-
-
-
-
-schema_name: str
-
- -
-
-entity1_logical_name: str
-
- -
-
-entity2_logical_name: str
-
- -
-
-intersect_entity_name: str | None = None
-
- -
-
-additional_properties: Dict[str, Any] | None = None
-
- -
-
-to_dict() Dict[str, Any]
-

Convert to Web API JSON format.

-

Example:

-
>>> rel = ManyToManyRelationshipMetadata(
-...     schema_name="new_account_contact",
-...     entity1_logical_name="account",
-...     entity2_logical_name="contact"
-... )
->>> rel.to_dict()
-{
-    '@odata.type': 'Microsoft.Dynamics.CRM.ManyToManyRelationshipMetadata',
-    'SchemaName': 'new_account_contact',
-    'Entity1LogicalName': 'account',
-    'Entity2LogicalName': 'contact',
-    'IntersectEntityName': 'new_account_contact'
-}
-
-
-
- -
- -
-
-class PowerPlatform.Dataverse.models.relationship.RelationshipInfo
-

Typed return model for relationship metadata.

-

Returned by create_one_to_many_relationship(), -create_many_to_many_relationship(), -get_relationship(), and -create_lookup_field().

-
-
Parameters:
-
    -
  • relationship_id (str or None) – Relationship metadata GUID.

  • -
  • relationship_schema_name (str) – Relationship schema name.

  • -
  • relationship_type (str) – Either "one_to_many" or "many_to_many".

  • -
  • lookup_schema_name (str or None) – Lookup field schema name (one-to-many only).

  • -
  • referenced_entity (str or None) – Parent entity logical name (one-to-many only).

  • -
  • referencing_entity (str or None) – Child entity logical name (one-to-many only).

  • -
  • entity1_logical_name (str or None) – First entity logical name (many-to-many only).

  • -
  • entity2_logical_name (str or None) – Second entity logical name (many-to-many only).

  • -
-
-
-

Example:

-
result = client.tables.create_one_to_many_relationship(lookup, relationship)
-print(result.relationship_schema_name)
-print(result.lookup_schema_name)
-
-
-
-
-relationship_id: str | None = None
-
- -
-
-relationship_schema_name: str = ''
-
- -
-
-relationship_type: str = ''
-
- -
-
-lookup_schema_name: str | None = None
-
- -
-
-referenced_entity: str | None = None
-
- -
-
-referencing_entity: str | None = None
-
- -
-
-entity1_logical_name: str | None = None
-
- -
-
-entity2_logical_name: str | None = None
-
- -
-
-classmethod from_one_to_many(*, relationship_id: str | None, relationship_schema_name: str, lookup_schema_name: str, referenced_entity: str, referencing_entity: str) RelationshipInfo
-

Create from a one-to-many relationship result.

-
-
Parameters:
-
    -
  • relationship_id (str or None) – Relationship metadata GUID.

  • -
  • relationship_schema_name (str) – Relationship schema name.

  • -
  • lookup_schema_name (str) – Lookup field schema name.

  • -
  • referenced_entity (str) – Parent entity logical name.

  • -
  • referencing_entity (str) – Child entity logical name.

  • -
-
-
Return type:
-

RelationshipInfo

-
-
-
- -
-
-classmethod from_many_to_many(*, relationship_id: str | None, relationship_schema_name: str, entity1_logical_name: str, entity2_logical_name: str) RelationshipInfo
-

Create from a many-to-many relationship result.

-
-
Parameters:
-
    -
  • relationship_id (str or None) – Relationship metadata GUID.

  • -
  • relationship_schema_name (str) – Relationship schema name.

  • -
  • entity1_logical_name (str) – First entity logical name.

  • -
  • entity2_logical_name (str) – Second entity logical name.

  • -
-
-
Return type:
-

RelationshipInfo

-
-
-
- -
-
-classmethod from_api_response(response_data: Dict[str, Any]) RelationshipInfo
-

Create from a raw Dataverse Web API response.

-

Detects one-to-many vs many-to-many from the @odata.type field -in the response and maps PascalCase keys to snake_case attributes. -Dataverse only supports these two relationship types; an unrecognized -@odata.type raises ValueError.

-
-
Parameters:
-

response_data (dict) – Raw relationship metadata from the Web API.

-
-
Return type:
-

RelationshipInfo

-
-
Raises:
-

ValueError – If the @odata.type is not a recognized -relationship type.

-
-
-
- -
- -
-
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/table_info/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/table_info/index.html deleted file mode 100644 index 26f39001..00000000 --- a/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/table_info/index.html +++ /dev/null @@ -1,403 +0,0 @@ - - - - - - - - PowerPlatform.Dataverse.models.table_info — PowerPlatform-Dataverse-Client 0.1.0b11 documentation - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

PowerPlatform.Dataverse.models.table_info

-

Table and column metadata models for Dataverse.

-
-

Classes

- - - - - - - - - - - - -

ColumnInfo

Column metadata from a Dataverse table definition.

TableInfo

Table metadata with dict-like backward compatibility.

AlternateKeyInfo

Alternate key metadata for a Dataverse table.

-
-
-

Module Contents

-
-
-class PowerPlatform.Dataverse.models.table_info.ColumnInfo
-

Column metadata from a Dataverse table definition.

-
-
Parameters:
-
    -
  • schema_name (str) – Column schema name (e.g. "new_Price").

  • -
  • logical_name (str) – Column logical name (lowercase).

  • -
  • type (str) – Column type string (e.g. "String", "Integer").

  • -
  • is_primary (bool) – Whether this is the primary name column.

  • -
  • is_required (bool) – Whether the column is required.

  • -
  • max_length (int or None) – Maximum length for string columns.

  • -
  • display_name (str or None) – Human-readable display name.

  • -
  • description (str or None) – Column description.

  • -
-
-
-
-
-schema_name: str = ''
-
- -
-
-logical_name: str = ''
-
- -
-
-type: str = ''
-
- -
-
-is_primary: bool = False
-
- -
-
-is_required: bool = False
-
- -
-
-max_length: int | None = None
-
- -
-
-display_name: str | None = None
-
- -
-
-description: str | None = None
-
- -
-
-classmethod from_api_response(response_data: Dict[str, Any]) ColumnInfo
-

Create from a raw Dataverse AttributeMetadata API response.

-
-
Parameters:
-

response_data (dict) – Raw attribute metadata dict (PascalCase keys).

-
-
Return type:
-

ColumnInfo

-
-
-
- -
- -
-
-class PowerPlatform.Dataverse.models.table_info.TableInfo
-

Table metadata with dict-like backward compatibility.

-

Supports both new attribute access (info.schema_name) and legacy -dict-key access (info["table_schema_name"]) for backward -compatibility with code written against the raw dict API.

-
-
Parameters:
-
    -
  • schema_name (str) – Table schema name (e.g. "Account").

  • -
  • logical_name (str) – Table logical name (lowercase).

  • -
  • entity_set_name (str) – OData entity set name.

  • -
  • metadata_id (str) – Metadata GUID.

  • -
  • display_name (str or None) – Human-readable display name.

  • -
  • description (str or None) – Table description.

  • -
  • columns (list[ColumnInfo] or None) – Column metadata (when retrieved).

  • -
  • columns_created (list[str] or None) – Column schema names created with the table.

  • -
-
-
-

Example:

-
info = client.tables.create("new_Product", {"new_Price": "decimal"})
-print(info.schema_name)              # new attribute access
-print(info["table_schema_name"])     # legacy dict-key access
-
-
-
-
-schema_name: str = ''
-
- -
-
-logical_name: str = ''
-
- -
-
-entity_set_name: str = ''
-
- -
-
-metadata_id: str = ''
-
- -
-
-primary_name_attribute: str | None = None
-
- -
-
-primary_id_attribute: str | None = None
-
- -
-
-display_name: str | None = None
-
- -
-
-description: str | None = None
-
- -
-
-columns: List[ColumnInfo] | None = None
-
- -
-
-columns_created: List[str] | None = None
-
- -
-
-get(key: str, default: Any = None) Any
-

Return value for key, or default if not present.

-
- -
-
-keys() KeysView[str]
-

Return legacy dict keys.

-
- -
-
-values() List[Any]
-

Return values corresponding to legacy dict keys.

-
- -
-
-items() List[tuple]
-

Return (legacy_key, value) pairs.

-
- -
-
-classmethod from_dict(data: Dict[str, Any]) TableInfo
-

Create from an SDK internal dict (snake_case keys).

-

This handles the dict format returned by _create_table and -_get_table_info in the OData layer.

-
-
Parameters:
-

data (dict) – Dictionary with SDK snake_case keys.

-
-
Return type:
-

TableInfo

-
-
-
- -
-
-classmethod from_api_response(response_data: Dict[str, Any]) TableInfo
-

Create from a raw Dataverse EntityDefinition API response.

-
-
Parameters:
-

response_data (dict) – Raw entity metadata dict (PascalCase keys).

-
-
Return type:
-

TableInfo

-
-
-
- -
-
-to_dict() Dict[str, Any]
-

Return a dict with legacy keys for backward compatibility.

-
- -
- -
-
-class PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo
-

Alternate key metadata for a Dataverse table.

-
-
Parameters:
-
    -
  • metadata_id (str) – Key metadata GUID.

  • -
  • schema_name (str) – Key schema name.

  • -
  • key_attributes (list[str]) – List of column logical names that compose the key.

  • -
  • status (str) – Index creation status ("Active", "Pending", "InProgress", "Failed").

  • -
-
-
-
-
-metadata_id: str = ''
-
- -
-
-schema_name: str = ''
-
- -
-
-key_attributes: List[str] = []
-
- -
-
-status: str = ''
-
- -
-
-classmethod from_api_response(response_data: Dict[str, Any]) AlternateKeyInfo
-

Create from raw EntityKeyMetadata API response.

-
-
Parameters:
-

response_data (dict) – Raw key metadata dictionary from the Web API.

-
-
Return type:
-

AlternateKeyInfo

-
-
-
- -
- -
-
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/upsert/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/upsert/index.html deleted file mode 100644 index cc9b7d70..00000000 --- a/docs_local/_build/autoapi/PowerPlatform/Dataverse/models/upsert/index.html +++ /dev/null @@ -1,171 +0,0 @@ - - - - - - - - PowerPlatform.Dataverse.models.upsert — PowerPlatform-Dataverse-Client 0.1.0b11 documentation - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

PowerPlatform.Dataverse.models.upsert

-

Upsert data models for the Dataverse SDK.

-
-

Classes

- - - - - - -

UpsertItem

Represents a single upsert operation targeting a record by its alternate key.

-
-
-

Module Contents

-
-
-class PowerPlatform.Dataverse.models.upsert.UpsertItem
-

Represents a single upsert operation targeting a record by its alternate key.

-

Used with upsert() -to upsert one or more records identified by alternate keys rather than primary GUIDs.

-
-
Parameters:
-
    -
  • alternate_key (dict[str, Any]) – Dictionary mapping alternate key attribute names to their values. -String values are automatically quoted and escaped in the OData URL. Integer and -other non-string values are included without quotes.

  • -
  • record (dict[str, Any]) – Dictionary of attribute names to values for the record payload. -Keys are automatically lowercased. Picklist labels are resolved to integer option -values when a matching option set is found.

  • -
-
-
-

Example:

-
item = UpsertItem(
-    alternate_key={"accountnumber": "ACC-001", "address1_postalcode": "98052"},
-    record={"name": "Contoso Ltd", "telephone1": "555-0100"},
-)
-
-
-
-
-alternate_key: Dict[str, Any]
-
- -
-
-record: Dict[str, Any]
-
- -
- -
-
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/batch/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/batch/index.html deleted file mode 100644 index b09e2f70..00000000 --- a/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/batch/index.html +++ /dev/null @@ -1,932 +0,0 @@ - - - - - - - - PowerPlatform.Dataverse.operations.batch — PowerPlatform-Dataverse-Client 0.1.0b11 documentation - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

PowerPlatform.Dataverse.operations.batch

-

Batch operation namespaces for the Dataverse SDK.

-
-

Classes

- - - - - - - - - - - - - - - - - - - - - - - - - - - -

ChangeSetRecordOperations

Record write operations available inside a ChangeSet.

ChangeSet

A transactional group of single-record write operations.

BatchRecordOperations

Record operations on a BatchRequest.

BatchTableOperations

Table metadata operations on a BatchRequest.

BatchQueryOperations

Query operations on a BatchRequest.

BatchDataFrameOperations

DataFrame-oriented wrappers for batch record operations.

BatchRequest

Builder for constructing and executing a Dataverse OData $batch request.

BatchOperations

Namespace for batch operations (client.batch).

-
-
-

Module Contents

-
-
-class PowerPlatform.Dataverse.operations.batch.ChangeSetRecordOperations(cs_internal: PowerPlatform.Dataverse.data._batch._ChangeSet)
-

Record write operations available inside a ChangeSet.

-

Mirrors client.records but restricted to single-record forms (no bulk -create/update/delete). Only write operations are allowed — GET is not -permitted inside a changeset.

-

Do not instantiate directly; use ChangeSet.records.

-
-
-create(table: str, data: Dict[str, Any]) str
-

Add a single-record create to this changeset.

-
-
Parameters:
-
    -
  • table (str) – Table schema name (e.g. "account").

  • -
  • data (dict[str, Any]) – Column values for the new record.

  • -
-
-
Returns:
-

A content-ID reference string (e.g. "$1") usable in -subsequent operations within this changeset as a URI reference -in @odata.bind fields or as record_id in -update() / delete().

-
-
Return type:
-

str

-
-
-

Example:

-
with batch.changeset() as cs:
-    lead_ref = cs.records.create("lead", {"firstname": "Ada"})
-    cs.records.create("account", {
-        "name": "Babbage",
-        "originatingleadid@odata.bind": lead_ref,
-    })
-
-
-
- -
-
-update(table: str, record_id: str, changes: Dict[str, Any]) None
-

Add a single-record update to this changeset.

-
-
Parameters:
-
    -
  • table (str) – Table schema name. Ignored when record_id is a -content-ID reference.

  • -
  • record_id (str) – GUID or a content-ID reference (e.g. "$1") -returned by a prior create() in this changeset.

  • -
  • changes (dict[str, Any]) – Column values to update.

  • -
-
-
-
- -
-
-delete(table: str, record_id: str) None
-

Add a single-record delete to this changeset.

-
-
Parameters:
-
    -
  • table (str) – Table schema name. Ignored when record_id is a -content-ID reference.

  • -
  • record_id (str) – GUID or a content-ID reference (e.g. "$1").

  • -
-
-
-
- -
- -
-
-class PowerPlatform.Dataverse.operations.batch.ChangeSet(internal: PowerPlatform.Dataverse.data._batch._ChangeSet)
-

A transactional group of single-record write operations.

-

All operations succeed or are rolled back together. Use as a context -manager or call records to add operations directly.

-

Do not instantiate directly; use BatchRequest.changeset().

-

Example:

-
with batch.changeset() as cs:
-    ref = cs.records.create("contact", {"firstname": "Alice"})
-    cs.records.update("account", account_id, {
-        "primarycontactid@odata.bind": ref
-    })
-
-
-
-
-records
-
- -
- -
-
-class PowerPlatform.Dataverse.operations.batch.BatchRecordOperations(batch: _BatchContext)
-

Record operations on a BatchRequest.

-

Mirrors client.records: same method names, same signatures. -All methods return None; results are available via -BatchResult after -BatchRequest.execute().

-

GA methods: retrieve() (single record) and list() (multi-record, -single page). get() is deprecated — use retrieve() instead.

-

Do not instantiate directly; use batch.records.

-
-
-create(table: str, data: Dict[str, Any] | List[Dict[str, Any]]) None
-

Add a create operation to the batch.

-

A single dict creates one record (POST entity_set). -A list of dicts creates all records via the CreateMultiple action -(one batch item).

-
-
Parameters:
-
    -
  • table (str) – Table schema name (e.g. "account").

  • -
  • data (dict or list[dict]) – Single record dict or list of record dicts.

  • -
-
-
-
- -
-
-update(table: str, ids: str | List[str], changes: Dict[str, Any] | List[Dict[str, Any]]) None
-

Add an update operation to the batch.

-
    -
  • Single (table, "guid", {...}) -> one PATCH request.

  • -
  • Broadcast (table, [id1, id2], {...}) -> one UpdateMultiple POST.

  • -
  • Paired (table, [id1, id2], [{...}, {...}]) -> one UpdateMultiple POST.

  • -
-
-
Parameters:
-
    -
  • table (str) – Table schema name.

  • -
  • ids (str or list[str]) – Single GUID or list of GUIDs.

  • -
  • changes (dict or list[dict]) – Single dict (single/broadcast) or list of dicts (paired).

  • -
-
-
-
- -
-
-delete(table: str, ids: str | List[str], *, use_bulk_delete: bool = True) None
-

Add a delete operation to the batch.

-
    -
  • Single (table, "guid") -> one DELETE request.

  • -
  • List + use_bulk_delete=True (default) -> one BulkDelete POST. -The async job ID will be available in BatchItemResponse.data["JobId"].

  • -
  • List + use_bulk_delete=False -> one DELETE per record.

  • -
-
-
Parameters:
-
    -
  • table (str) – Table schema name.

  • -
  • ids (str or list[str]) – Single GUID or list of GUIDs.

  • -
  • use_bulk_delete (bool) – When True (default) and ids is a list, use the -BulkDelete action. When False, delete records individually.

  • -
-
-
-
- -
-
-get(table: str, record_id: str, *, select: List[str] | None = None) None
-

Add a single-record get operation to the batch.

-
-

Deprecated since version Use: retrieve() instead. batch.records.get() is deprecated -and will be removed in a future release.

-
-
-
Parameters:
-
    -
  • table (str) – Table schema name.

  • -
  • record_id (str) – GUID of the record to retrieve.

  • -
  • select (list[str] or None) – Optional list of column names to include.

  • -
-
-
-
- -
-
-upsert(table: str, items: List[PowerPlatform.Dataverse.models.upsert.UpsertItem | Dict[str, Any]]) None
-

Add an upsert operation to the batch.

-

Mirrors upsert(): -a single item becomes a PATCH request using the alternate key; multiple items -become one UpsertMultiple POST.

-

Each item must be a UpsertItem -or a plain dict with "alternate_key" and "record" keys (both dicts).

-
-
Parameters:
-
    -
  • table (str) – Table schema name (e.g. "account").

  • -
  • items (list[UpsertItem]) – Non-empty list of UpsertItem -instances or equivalent dicts.

  • -
-
-
Raises:
-

TypeError – If items is not a non-empty list, or if any element is -neither a UpsertItem nor a -dict with "alternate_key" and "record" keys.

-
-
-

Example:

-
from PowerPlatform.Dataverse.models import UpsertItem
-
-batch.records.upsert("account", [
-    UpsertItem(
-        alternate_key={"accountnumber": "ACC-001"},
-        record={"name": "Contoso Ltd"},
-    ),
-    UpsertItem(
-        alternate_key={"accountnumber": "ACC-002"},
-        record={"name": "Fabrikam Inc"},
-    ),
-])
-
-
-
- -
-
-retrieve(table: str, record_id: str, *, select: List[str] | None = None, expand: List[str] | None = None, include_annotations: str | None = None) None
-

Add a single-record retrieve operation to the batch.

-

GA replacement for the deprecated get(). Enqueues a GET request -for one record by its GUID. The response body will be available in -data -after BatchRequest.execute().

-
-
Parameters:
-
    -
  • table (str) – Table schema name (e.g. "account").

  • -
  • record_id (str) – GUID of the record to retrieve.

  • -
  • select (list[str] or None) – Optional list of column logical names to include.

  • -
  • expand (list[str] or None) – Optional list of navigation properties to expand. -Navigation property names are case-sensitive and must match the -entity’s $metadata.

  • -
  • include_annotations (str or None) – OData annotation pattern for the -Prefer: odata.include-annotations header (e.g. "*" or -"OData.Community.Display.V1.FormattedValue"), or None.

  • -
-
-
-

Example:

-
batch = client.batch.new()
-batch.records.retrieve(
-    "account", account_id,
-    select=["name", "statuscode"],
-    expand=["primarycontactid"],
-    include_annotations="OData.Community.Display.V1.FormattedValue",
-)
-result = batch.execute()
-record = result.responses[0].data
-contact = (record.get("primarycontactid") or {})
-print(contact.get("fullname"))
-
-
-
- -
-
-list(table: str, *, filter: str | FilterExpression | None = None, select: List[str] | None = None, orderby: List[str] | None = None, top: int | None = None, expand: List[str] | None = None, page_size: int | None = None, count: bool = False, include_annotations: str | None = None) None
-

Add a multi-record list operation to the batch (single page, no pagination).

-

Enqueues a GET request for multiple records. Because batch requests are -a single HTTP round-trip, pagination (@odata.nextLink) is not -supported — use top to bound the result size, or rely on the -server’s default page limit.

-

The response body ({"value": [...]} JSON) will be available in -data -after BatchRequest.execute().

-
-
Parameters:
-
    -
  • table (str) – Table schema name (e.g. "account").

  • -
  • filter (str or FilterExpression or None) – Optional OData $filter expression or FilterExpression.

  • -
  • select (list[str] or None) – Optional list of column logical names to include.

  • -
  • orderby (list[str] or None) – Optional list of sort expressions (e.g. ["name asc"]).

  • -
  • top (int or None) – Maximum number of records to return.

  • -
  • expand (list[str] or None) – Optional list of navigation properties to expand.

  • -
  • page_size (int or None) – Per-page size hint via Prefer: odata.maxpagesize.

  • -
  • count (bool) – If True, adds $count=true to the request.

  • -
  • include_annotations (str or None) – OData annotation pattern for the -Prefer: odata.include-annotations header, or None.

  • -
-
-
-

Example:

-
batch = client.batch.new()
-batch.records.list(
-    "account",
-    filter="statecode eq 0",
-    select=["name", "statuscode"],
-    orderby=["name asc"],
-    top=50,
-    include_annotations="OData.Community.Display.V1.FormattedValue",
-)
-result = batch.execute()
-records = result.responses[0].data.get("value", [])
-
-
-
- -
- -
-
-class PowerPlatform.Dataverse.operations.batch.BatchTableOperations(batch: _BatchContext)
-

Table metadata operations on a BatchRequest.

-

Mirrors client.tables exactly: same method names, same signatures. -All methods return None; results arrive via -BatchResult.

-
-

Note

-

tables.delete, tables.add_columns, and tables.remove_columns -require a metadata lookup (GET EntityDefinitions) at -BatchRequest.execute() time to resolve the table’s MetadataId. -This lookup is transparent to the caller.

-
-
-

Note

-

tables.add_columns and tables.remove_columns each produce one -batch item per column, so they contribute multiple entries to -responses.

-
-

Do not instantiate directly; use batch.tables.

-
-
-create(table: str, columns: Dict[str, Any], *, solution: str | None = None, primary_column: str | None = None, display_name: str | None = None) None
-

Add a table-create operation to the batch.

-
-

Note

-

The pre-existence check performed by client.tables.create is skipped -in batch mode. If the table already exists the server returns an error -in the corresponding BatchItemResponse.

-
-
-
Parameters:
-
    -
  • table (str) – Schema name of the new table (e.g. "new_Product").

  • -
  • columns (dict[str, Any]) – Mapping of column schema names to type strings or Enum subclasses.

  • -
  • solution (str or None) – Optional solution unique name.

  • -
  • primary_column (str or None) – Optional primary column schema name.

  • -
  • display_name (str or None) – Human-readable display name for the table. -When omitted, defaults to the table schema name.

  • -
-
-
-
- -
-
-delete(table: str) None
-

Add a table-delete operation to the batch.

-

The table’s MetadataId is resolved via a GET request at execute time.

-
-
Parameters:
-

table (str) – Schema name of the table to delete.

-
-
-
- -
-
-get(table: str) None
-

Add a table-metadata-get operation to the batch.

-

The response will be in BatchItemResponse.data after execute.

-
-
Parameters:
-

table (str) – Schema name of the table.

-
-
-
- -
-
-list(*, filter: str | None = None, select: List[str] | None = None) None
-

Add a list-all-tables operation to the batch.

-

Mirrors client.tables.list(). Supply an optional OData -$filter expression to further narrow the results (combined with -IsPrivate eq false using and). select projects -specific property names via $select.

-

The response will be in BatchItemResponse.data after execute.

-
-
Parameters:
-
    -
  • filter (str or None) – Additional OData $filter expression.

  • -
  • select (list[str] or None) – List of property names for $select.

  • -
-
-
-
- -
-
-add_columns(table: str, columns: Dict[str, Any]) None
-

Add column-create operations to the batch (one per column).

-

The table’s MetadataId is resolved at execute time. Each column -produces one entry in responses.

-
-
Parameters:
-
    -
  • table (str) – Schema name of the target table.

  • -
  • columns (dict[str, Any]) – Mapping of column schema names to type strings or Enum subclasses.

  • -
-
-
-
- -
-
-remove_columns(table: str, columns: str | List[str]) None
-

Add column-delete operations to the batch (one per column).

-

The table’s MetadataId and each column’s MetadataId are resolved -at execute time. Each column produces one entry in -responses.

-
-
Parameters:
-
    -
  • table (str) – Schema name of the target table.

  • -
  • columns (str or list[str]) – Column schema name or list of column schema names to remove.

  • -
-
-
-
- -
-
-create_one_to_many_relationship(lookup: PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata, relationship: PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata, *, solution: str | None = None) None
-

Add a one-to-many relationship creation to the batch.

-
-
Parameters:
-
-
-
-
- -
-
-create_many_to_many_relationship(relationship: PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata, *, solution: str | None = None) None
-

Add a many-to-many relationship creation to the batch.

-
-
Parameters:
-
-
-
-
- -
-
-delete_relationship(relationship_id: str) None
-

Add a relationship-delete operation to the batch.

-
-
Parameters:
-

relationship_id (str) – GUID of the relationship metadata to delete.

-
-
-
- -
-
-get_relationship(schema_name: str) None
-

Add a relationship-metadata-get operation to the batch.

-

The response will be in BatchItemResponse.data after execute.

-
-
Parameters:
-

schema_name (str) – Schema name of the relationship.

-
-
-
- -
-
-create_lookup_field(referencing_table: str, lookup_field_name: str, referenced_table: str, *, display_name: str | None = None, description: str | None = None, required: bool = False, cascade_delete: str = CASCADE_BEHAVIOR_REMOVE_LINK, solution: str | None = None, language_code: int = 1033) None
-

Add a lookup field creation to the batch (convenience wrapper for -create_one_to_many_relationship()).

-
-
Parameters:
-
    -
  • referencing_table (str) – Logical name of the child (many) table.

  • -
  • lookup_field_name (str) – Schema name for the lookup field.

  • -
  • referenced_table (str) – Logical name of the parent (one) table.

  • -
  • display_name (str or None) – Display name for the lookup field.

  • -
  • description (str or None) – Optional description.

  • -
  • required (bool) – Whether the lookup is required.

  • -
  • cascade_delete (str) – Delete cascade behaviour.

  • -
  • solution (str or None) – Optional solution unique name.

  • -
  • language_code (int) – Language code for labels (default 1033).

  • -
-
-
-
- -
- -
-
-class PowerPlatform.Dataverse.operations.batch.BatchQueryOperations(batch: _BatchContext)
-

Query operations on a BatchRequest.

-

Mirrors client.query exactly: same method names, same signatures. -All methods return None; results arrive via -BatchResult.

-

Do not instantiate directly; use batch.query.

-
-
-sql(sql: str) None
-

Add a SQL SELECT query to the batch.

-

Mirrors sql(). -The entity set is resolved from the table name in the SQL statement at -BatchRequest.execute() time.

-
-
Parameters:
-

sql (str) – A single SELECT statement within the Dataverse-supported subset.

-
-
Raises:
-

ValidationError – If sql is not a non-empty string.

-
-
-

Example:

-
batch.query.sql("SELECT accountid, name FROM account WHERE name = 'Contoso'")
-
-
-
- -
- -
-
-class PowerPlatform.Dataverse.operations.batch.BatchDataFrameOperations(batch: _BatchContext)
-

DataFrame-oriented wrappers for batch record operations.

-

Provides create(), update(), and delete() that accept -pandas.DataFrame / pandas.Series inputs and convert them to standard -dicts before enqueueing on the batch. This lets data-science callers feed -DataFrames directly into a batch without manual conversion.

-

Accessed via batch.dataframe.

-

Example:

-
import pandas as pd
-
-batch = client.batch.new()
-df = pd.DataFrame([
-    {"name": "Contoso", "telephone1": "555-0100"},
-    {"name": "Fabrikam", "telephone1": "555-0200"},
-])
-batch.dataframe.create("account", df)
-result = batch.execute()
-
-
-
-
-create(table: str, records: pandas.DataFrame) None
-

Enqueue record creates from a pandas DataFrame.

-

Each row becomes a record. All rows are bundled in a single -CreateMultiple batch item (one HTTP request in the batch).

-
-
Parameters:
-
    -
  • table (str) – Table schema name (e.g. "account").

  • -
  • records (DataFrame) – DataFrame where each row is a record to create.

  • -
-
-
Raises:
-
    -
  • TypeError – If records is not a pandas DataFrame.

  • -
  • ValueError – If records is empty or any row has no non-null values.

  • -
-
-
-

Example:

-
df = pd.DataFrame([{"name": "Contoso"}, {"name": "Fabrikam"}])
-batch.dataframe.create("account", df)
-
-
-
- -
-
-update(table: str, changes: pandas.DataFrame, id_column: str, clear_nulls: bool = False) None
-

Enqueue record updates from a pandas DataFrame.

-

Each row represents an update. The id_column specifies which -column contains the record GUIDs.

-
-
Parameters:
-
    -
  • table (str) – Table schema name (e.g. "account").

  • -
  • changes (DataFrame) – DataFrame where each row contains a record GUID and -the fields to update.

  • -
  • id_column (str) – Name of the DataFrame column containing record GUIDs.

  • -
  • clear_nulls (bool) – When False (default), NaN/None values are -skipped. When True, NaN/None sends null to clear the field.

  • -
-
-
Raises:
-
    -
  • TypeError – If changes is not a pandas DataFrame.

  • -
  • ValueError – If changes is empty, id_column is missing, -or IDs are invalid.

  • -
-
-
-

Example:

-
df = pd.DataFrame([
-    {"accountid": "guid-1", "telephone1": "555-0100"},
-    {"accountid": "guid-2", "telephone1": "555-0200"},
-])
-batch.dataframe.update("account", df, id_column="accountid")
-
-
-
- -
-
-delete(table: str, ids: pandas.Series, use_bulk_delete: bool = True) None
-

Enqueue record deletes from a pandas Series of GUIDs.

-
-
Parameters:
-
    -
  • table (str) – Table schema name (e.g. "account").

  • -
  • ids (Series) – Series of record GUIDs to delete.

  • -
  • use_bulk_delete (bool) – When True (default) and ids has multiple -values, use the BulkDelete action.

  • -
-
-
Raises:
-
    -
  • TypeError – If ids is not a pandas Series.

  • -
  • ValueError – If ids contains invalid values.

  • -
-
-
-

Example:

-
ids_series = pd.Series(["guid-1", "guid-2", "guid-3"])
-batch.dataframe.delete("account", ids_series)
-
-
-
- -
- -
-
-class PowerPlatform.Dataverse.operations.batch.BatchRequest(client: PowerPlatform.Dataverse.client.DataverseClient)
-

Builder for constructing and executing a Dataverse OData $batch request.

-

Obtain via BatchOperations.new() (client.batch.new()). Add operations -through records, tables, query, and dataframe, -optionally group writes -into a changeset(), then call execute().

-

Operations are executed sequentially in the order added. The resulting -BatchResult contains one -BatchItemResponse per HTTP -request dispatched (some operations expand to multiple requests).

-
-

Note

-

Maximum 1000 HTTP operations per batch.

-
-

Example:

-
batch = client.batch.new()
-batch.records.create("account", {"name": "Contoso"})
-batch.tables.get("account")
-with batch.changeset() as cs:
-    ref = cs.records.create("contact", {"firstname": "Alice"})
-    cs.records.update("account", account_id, {
-        "primarycontactid@odata.bind": ref
-    })
-result = batch.execute()
-
-
-
-
-records
-
- -
-
-tables
-
- -
-
-query
-
- -
-
-dataframe
-
- -
-
-changeset() ChangeSet
-

Create a new ChangeSet attached to this batch.

-

The changeset is added to the batch immediately. Operations added to -the returned ChangeSet via cs.records.* execute atomically.

-
-
Returns:
-

A new ChangeSet ready to receive operations.

-
-
-

Example:

-
with batch.changeset() as cs:
-    cs.records.create("account", {"name": "ACME"})
-    cs.records.create("contact", {"firstname": "Bob"})
-
-
-
- -
-
-execute(*, continue_on_error: bool = False) PowerPlatform.Dataverse.models.batch.BatchResult
-

Submit the batch to Dataverse and return all responses.

-
-
Parameters:
-

continue_on_error – When False (default), Dataverse stops at the -first failure and returns that operation’s error as a 4xx response. -When True, Prefer: odata.continue-on-error is sent and all -operations are attempted.

-
-
Returns:
-

BatchResult -with one entry per HTTP operation in submission order.

-
-
Raises:
-
    -
  • ValidationError – If the batch exceeds 1000 operations or an -unsupported column type is specified.

  • -
  • MetadataError – If metadata pre-resolution fails (table or -column not found) for tables.delete, tables.add_columns, -or tables.remove_columns.

  • -
  • HttpError – On HTTP-level failures (auth, server error, etc.) -that prevent the batch from executing.

  • -
-
-
-
- -
- -
-
-class PowerPlatform.Dataverse.operations.batch.BatchOperations(client: PowerPlatform.Dataverse.client.DataverseClient)
-

Namespace for batch operations (client.batch).

-

Accessed via client.batch. Use new() to create a -BatchRequest builder.

-
-
Parameters:
-

client – The parent DataverseClient instance.

-
-
-

Example:

-
batch = client.batch.new()
-batch.records.create("account", {"name": "Fabrikam"})
-result = batch.execute()
-
-
-
-
-new() BatchRequest
-

Create a new empty BatchRequest builder.

-
-
Returns:
-

An empty BatchRequest.

-
-
-
- -
- -
-
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/dataframe/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/dataframe/index.html deleted file mode 100644 index 2a63021b..00000000 --- a/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/dataframe/index.html +++ /dev/null @@ -1,413 +0,0 @@ - - - - - - - - PowerPlatform.Dataverse.operations.dataframe — PowerPlatform-Dataverse-Client 0.1.0b11 documentation - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

PowerPlatform.Dataverse.operations.dataframe

-

DataFrame CRUD operations namespace for the Dataverse SDK.

-
-

Classes

- - - - - - -

DataFrameOperations

Namespace for pandas DataFrame CRUD operations.

-
-
-

Module Contents

-
-
-class PowerPlatform.Dataverse.operations.dataframe.DataFrameOperations(client: PowerPlatform.Dataverse.client.DataverseClient)
-

Namespace for pandas DataFrame CRUD operations.

-

Accessed via client.dataframe. Provides DataFrame-oriented wrappers -around the record-level CRUD operations.

-
-
Parameters:
-

client (DataverseClient) – The parent DataverseClient instance.

-
-
-

Example:

-
import pandas as pd
-
-client = DataverseClient(base_url, credential)
-
-# Query records as a DataFrame
-df = client.dataframe.get("account", select=["name"], top=100)
-
-# Create records from a DataFrame
-new_df = pd.DataFrame([{"name": "Contoso"}, {"name": "Fabrikam"}])
-new_df["accountid"] = client.dataframe.create("account", new_df)
-
-# Update records
-new_df["telephone1"] = ["555-0100", "555-0200"]
-client.dataframe.update("account", new_df, id_column="accountid")
-
-# Delete records
-client.dataframe.delete("account", new_df["accountid"])
-
-
-
-
-sql(sql: str) pandas.DataFrame
-

Execute a SQL query and return the results as a pandas DataFrame.

-

Delegates to sql() -and converts the list of records into a single DataFrame.

-
-
Parameters:
-

sql (str) – Supported SQL SELECT statement.

-
-
Returns:
-

DataFrame containing all result rows. Returns an empty -DataFrame when no rows match.

-
-
Return type:
-

DataFrame

-
-
Raises:
-

ValidationError – If sql is not a string or is empty.

-
-
-

Example

-

SQL query to DataFrame:

-
df = client.dataframe.sql(
-    "SELECT TOP 100 name, revenue FROM account "
-    "WHERE statecode = 0 ORDER BY revenue"
-)
-print(f"Got {len(df)} rows")
-print(df.head())
-
-
-

Aggregate query to DataFrame:

-
df = client.dataframe.sql(
-    "SELECT a.name, COUNT(c.contactid) as cnt "
-    "FROM account a "
-    "JOIN contact c ON a.accountid = c.parentcustomerid "
-    "GROUP BY a.name"
-)
-
-
-
- -
-
-get(table: str, record_id: str | None = None, select: List[str] | None = None, filter: str | None = None, orderby: List[str] | None = None, top: int | None = None, expand: List[str] | None = None, page_size: int | None = None, count: bool = False, include_annotations: str | None = None) pandas.DataFrame
-

Fetch records and return as a single pandas DataFrame.

-

When record_id is provided, returns a single-row DataFrame. -When record_id is None, internally iterates all pages and returns one -consolidated DataFrame.

-
-
Parameters:
-
    -
  • table (str) – Schema name of the table (e.g. "account" or "new_MyTestTable").

  • -
  • record_id (str or None) – Optional GUID to fetch a specific record. If None, queries multiple records.

  • -
  • select (list[str] or None) – Optional list of attribute logical names to retrieve.

  • -
  • filter (str or None) – Optional OData filter string. Column names must use exact lowercase logical names.

  • -
  • orderby (list[str] or None) – Optional list of attributes to sort by.

  • -
  • top (int or None) – Optional maximum number of records to return.

  • -
  • expand (list[str] or None) – Optional list of navigation properties to expand (case-sensitive).

  • -
  • page_size (int or None) – Optional number of records per page for pagination.

  • -
  • count (bool) – If True, adds $count=true to include a total -record count in the response.

  • -
  • include_annotations (str or None) – OData annotation pattern for the -Prefer: odata.include-annotations header (e.g. "*" or -"OData.Community.Display.V1.FormattedValue"), or None.

  • -
-
-
Returns:
-

DataFrame containing all matching records. Returns an empty DataFrame -when no records match.

-
-
Return type:
-

DataFrame

-
-
Raises:
-

ValueError – If record_id is not a non-empty string, or if -query parameters (filter, orderby, top, expand, -page_size) are provided alongside record_id.

-
-
-
-

Tip

-

For large tables, use top or filter to limit the result set.

-
-

Example

-

Fetch a single record as a DataFrame:

-
df = client.dataframe.get("account", record_id=account_id, select=["name", "telephone1"])
-print(df)
-
-
-

Query with filtering:

-
df = client.dataframe.get("account", filter="statecode eq 0", select=["name"])
-print(f"Got {len(df)} active accounts")
-
-
-

Limit result size:

-
df = client.dataframe.get("account", select=["name"], top=100)
-
-
-
- -
-
-create(table: str, records: pandas.DataFrame) pandas.Series
-

Create records from a pandas DataFrame.

-
-
Parameters:
-
    -
  • table (str) – Schema name of the table (e.g. "account" or "new_MyTestTable").

  • -
  • records (DataFrame) – DataFrame where each row is a record to create.

  • -
-
-
Returns:
-

Series of created record GUIDs, aligned with the input DataFrame index.

-
-
Return type:
-

Series

-
-
Raises:
-
    -
  • TypeError – If records is not a pandas DataFrame.

  • -
  • ValueError – If records is empty or the number of returned -IDs does not match the number of input rows.

  • -
-
-
-
-

Tip

-

All rows are sent in a single CreateMultiple request. For very -large DataFrames, consider splitting into smaller batches to avoid -request timeouts.

-
-

Example

-

Create records from a DataFrame:

-
import pandas as pd
-
-df = pd.DataFrame([
-    {"name": "Contoso", "telephone1": "555-0100"},
-    {"name": "Fabrikam", "telephone1": "555-0200"},
-])
-df["accountid"] = client.dataframe.create("account", df)
-
-
-
- -
-
-update(table: str, changes: pandas.DataFrame, id_column: str, clear_nulls: bool = False) None
-

Update records from a pandas DataFrame.

-

Each row in the DataFrame represents an update. The id_column specifies which -column contains the record GUIDs.

-
-
Parameters:
-
    -
  • table (str) – Schema name of the table (e.g. "account" or "new_MyTestTable").

  • -
  • changes (DataFrame) – DataFrame where each row contains a record GUID and the fields to update.

  • -
  • id_column (str) – Name of the DataFrame column containing record GUIDs.

  • -
  • clear_nulls (bool) – When False (default), missing values (NaN/None) are skipped -(the field is left unchanged on the server). When True, missing values are sent -as null to Dataverse, clearing the field. Use True only when you intentionally -want NaN/None values to clear fields.

  • -
-
-
Raises:
-
    -
  • TypeError – If changes is not a pandas DataFrame.

  • -
  • ValueError – If changes is empty, id_column is not found in the -DataFrame, id_column contains invalid (non-string, empty, or whitespace-only) -values, or no updatable columns exist besides id_column. -When clear_nulls is False (default), rows where all change values -are NaN/None produce empty patches and are silently skipped. If all rows -are skipped, the method returns without making an API call. When -clear_nulls is True, NaN/None values become explicit nulls, so -rows are never skipped.

  • -
-
-
-
-

Tip

-

All rows are sent in a single UpdateMultiple request (or a -single PATCH for one row). For very large DataFrames, consider -splitting into smaller batches to avoid request timeouts.

-
-

Example

-

Update records with different values per row:

-
import pandas as pd
-
-df = pd.DataFrame([
-    {"accountid": "guid-1", "telephone1": "555-0100"},
-    {"accountid": "guid-2", "telephone1": "555-0200"},
-])
-client.dataframe.update("account", df, id_column="accountid")
-
-
-

Broadcast the same change to all records:

-
df = pd.DataFrame({"accountid": ["guid-1", "guid-2", "guid-3"]})
-df["websiteurl"] = "https://example.com"
-client.dataframe.update("account", df, id_column="accountid")
-
-
-

Clear a field by setting clear_nulls=True:

-
df = pd.DataFrame([{"accountid": "guid-1", "websiteurl": None}])
-client.dataframe.update("account", df, id_column="accountid", clear_nulls=True)
-
-
-
- -
-
-delete(table: str, ids: pandas.Series, use_bulk_delete: bool = True) str | None
-

Delete records by passing a pandas Series of GUIDs.

-
-
Parameters:
-
    -
  • table (str) – Schema name of the table (e.g. "account" or "new_MyTestTable").

  • -
  • ids (Series) – Series of record GUIDs to delete.

  • -
  • use_bulk_delete (bool) – When True (default) and ids contains multiple values, execute the BulkDelete -action and return its async job identifier. When False each record is deleted sequentially.

  • -
-
-
Raises:
-
    -
  • TypeError – If ids is not a pandas Series.

  • -
  • ValueError – If ids contains invalid (non-string, empty, or -whitespace-only) values.

  • -
-
-
Returns:
-

BulkDelete job ID when deleting multiple records via BulkDelete; -None when deleting a single record, using sequential deletion, or -when ids is empty.

-
-
Return type:
-

str or None

-
-
-

Example

-

Delete records using a Series:

-
import pandas as pd
-
-ids = pd.Series(["guid-1", "guid-2", "guid-3"])
-client.dataframe.delete("account", ids)
-
-
-
- -
- -
-
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/files/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/files/index.html deleted file mode 100644 index de856206..00000000 --- a/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/files/index.html +++ /dev/null @@ -1,212 +0,0 @@ - - - - - - - - PowerPlatform.Dataverse.operations.files — PowerPlatform-Dataverse-Client 0.1.0b11 documentation - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

PowerPlatform.Dataverse.operations.files

-

File operations namespace for the Dataverse SDK.

-
-

Classes

- - - - - - -

FileOperations

Namespace for file operations.

-
-
-

Module Contents

-
-
-class PowerPlatform.Dataverse.operations.files.FileOperations(client: PowerPlatform.Dataverse.client.DataverseClient)
-

Namespace for file operations.

-

Accessed via client.files. Provides file upload operations for -Dataverse file columns.

-
-
Parameters:
-

client (DataverseClient) – The parent DataverseClient instance.

-
-
-

Example:

-
client = DataverseClient(base_url, credential)
-
-client.files.upload(
-    "account", account_id, "new_Document", "/path/to/file.pdf"
-)
-
-
-
-
-upload(table: str, record_id: str, file_column: str, path: str, *, mode: str | None = None, mime_type: str | None = None, if_none_match: bool = True) None
-

Upload a file to a Dataverse file column.

-
-
Parameters:
-
    -
  • table (str) – Schema name of the table (e.g. "account" or -"new_MyTestTable").

  • -
  • record_id (str) – GUID of the target record.

  • -
  • file_column (str) – Schema name of the file column attribute (e.g., -"new_Document"). If the column doesn’t exist, it will be -created automatically.

  • -
  • path (str) – Local filesystem path to the file. The stored filename -will be the basename of this path.

  • -
  • mode (str or None) – Upload strategy: "auto" (default), "small", or -"chunk". Auto mode selects small or chunked upload based on -file size.

  • -
  • mime_type (str or None) – Explicit MIME type to store with the file (e.g. -"application/pdf"). If not provided, defaults to -"application/octet-stream".

  • -
  • if_none_match (bool) – When True (default), sends -If-None-Match: null header to only succeed if the column is -currently empty. Set False to always overwrite using -If-Match: *.

  • -
-
-
Raises:
-
    -
  • HttpError – If the upload fails or the file column is not empty when -if_none_match=True.

  • -
  • FileNotFoundError – If the specified file path does not exist.

  • -
-
-
-

Example

-

Upload a PDF file:

-
client.files.upload(
-    "account",
-    account_id,
-    "new_Contract",
-    "/path/to/contract.pdf",
-    mime_type="application/pdf",
-)
-
-
-

Upload with auto mode selection:

-
client.files.upload(
-    "email",
-    email_id,
-    "new_Attachment",
-    "/path/to/large_file.zip",
-)
-
-
-
- -
- -
-
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/index.html deleted file mode 100644 index 238598f2..00000000 --- a/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/index.html +++ /dev/null @@ -1,134 +0,0 @@ - - - - - - - - PowerPlatform.Dataverse.operations — PowerPlatform-Dataverse-Client 0.1.0b11 documentation - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

PowerPlatform.Dataverse.operations

-

Operation namespaces for the Dataverse SDK.

-

This module contains the operation namespace classes that organize -SDK operations into logical groups: records, query, and tables.

-
-

Submodules

- -
-
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/query/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/query/index.html deleted file mode 100644 index 05372f0d..00000000 --- a/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/query/index.html +++ /dev/null @@ -1,497 +0,0 @@ - - - - - - - - PowerPlatform.Dataverse.operations.query — PowerPlatform-Dataverse-Client 0.1.0b11 documentation - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

PowerPlatform.Dataverse.operations.query

-

Query operations namespace for the Dataverse SDK.

-
-

Classes

- - - - - - -

QueryOperations

Namespace for query operations.

-
-
-

Module Contents

-
-
-class PowerPlatform.Dataverse.operations.query.QueryOperations(client: PowerPlatform.Dataverse.client.DataverseClient)
-

Namespace for query operations.

-

Accessed via client.query. Provides query and search operations -against Dataverse tables.

-
-
Parameters:
-

client (DataverseClient) – The parent DataverseClient instance.

-
-
-

Example:

-
from PowerPlatform.Dataverse.models.filters import col
-
-client = DataverseClient(base_url, credential)
-
-# Fluent query builder (recommended)
-for record in (client.query.builder("account")
-               .select("name", "revenue")
-               .where(col("statecode") == 0)
-               .order_by("revenue", descending=True)
-               .top(100)
-               .execute()):
-    print(record["name"])
-
-# SQL query
-rows = client.query.sql("SELECT TOP 10 name FROM account ORDER BY name")
-for row in rows:
-    print(row["name"])
-
-
-
-
-builder(table: str) PowerPlatform.Dataverse.models.query_builder.QueryBuilder
-

Create a fluent query builder for the specified table.

-

Returns a QueryBuilder -that can be chained with filter, select, and order methods, then -executed directly via .execute().

-
-
Parameters:
-

table (str) – Table schema name (e.g. "account").

-
-
Returns:
-

A QueryBuilder instance bound to this client.

-
-
Return type:
-

QueryBuilder

-
-
-

Example

-

Build and execute a query fluently:

-
from PowerPlatform.Dataverse.models.filters import col
-
-for record in (client.query.builder("account")
-               .select("name", "revenue")
-               .where(col("statecode") == 0)
-               .where(col("revenue") > 1_000_000)
-               .order_by("revenue", descending=True)
-               .top(100)
-               .page_size(50)
-               .execute()):
-    print(record["name"])
-
-
-

With composable expression tree:

-
from PowerPlatform.Dataverse.models.filters import col
-
-for record in (client.query.builder("account")
-               .where((col("statecode") == 0) | (col("statecode") == 1))
-               .where(col("revenue") > 100_000)
-               .execute()):
-    print(record["name"])
-
-
-
- -
-
-sql(sql: str) List[PowerPlatform.Dataverse.models.record.Record]
-

Execute a read-only SQL query using the Dataverse Web API.

-

The Dataverse SQL endpoint supports a broad subset of T-SQL:

-
SELECT / SELECT DISTINCT / SELECT TOP N (0-5000)
-FROM table [alias]
-INNER JOIN / LEFT JOIN (multi-table, no depth limit)
-WHERE (=, !=, >, <, >=, <=, LIKE, IN, NOT IN, IS NULL,
-       IS NOT NULL, BETWEEN, AND, OR, nested parentheses)
-GROUP BY column
-ORDER BY column [ASC|DESC]
-OFFSET n ROWS FETCH NEXT m ROWS ONLY
-COUNT(*), SUM(), AVG(), MIN(), MAX()
-
-
-

SELECT * is not supported – specify column names explicitly. -Use sql_columns() to discover available column names for a table.

-

Not supported: SELECT *, subqueries, CTE, HAVING, UNION, -RIGHT/FULL/CROSS JOIN, CASE, COALESCE, window functions, -string/date/math functions, INSERT/UPDATE/DELETE. For writes, use -client.records methods.

-
-
Parameters:
-

sql (str) – Supported SQL SELECT statement.

-
-
Returns:
-

List of Record -objects. Returns an empty list when no rows match.

-
-
Return type:
-

list[Record]

-
-
Raises:
-

ValidationError – If sql is not a string or is empty.

-
-
-

Example

-

Basic query:

-
rows = client.query.sql(
-    "SELECT TOP 10 name FROM account ORDER BY name"
-)
-
-
-

JOIN with aggregation:

-
rows = client.query.sql(
-    "SELECT a.name, COUNT(c.contactid) as cnt "
-    "FROM account a "
-    "JOIN contact c ON a.accountid = c.parentcustomerid "
-    "GROUP BY a.name"
-)
-
-
-
- -
-
-fetchxml(xml: str) PowerPlatform.Dataverse.models.fetchxml_query.FetchXmlQuery
-

Return an inert FetchXmlQuery object.

-

No HTTP request is made until -execute() -or -execute_pages() -is called on the returned object.

-

Use for SQL-JOIN scenarios, aggregate queries, or other operations that -the OData builder endpoint cannot express.

-
-
Parameters:
-

xml (str) – Well-formed FetchXML query string. The root <entity name="..."> -element determines the entity set endpoint.

-
-
Returns:
-

Inert query object with .execute() and .execute_pages() methods.

-
-
Return type:
-

FetchXmlQuery

-
-
Raises:
-

ValueError – If the FetchXML is missing a root <entity> element -or the entity name attribute.

-
-
-

Example:

-
query = client.query.fetchxml("""
-  <fetch top="50">
-    <entity name="account">
-      <attribute name="name" />
-      <link-entity name="contact" from="parentcustomerid"
-                   to="accountid" alias="c" link-type="inner">
-        <attribute name="fullname" />
-      </link-entity>
-    </entity>
-  </fetch>
-""")
-
-# Eager — collect all pages:
-result = query.execute()
-df = result.to_dataframe()
-
-# Lazy — process one page at a time:
-for page in query.execute_pages():
-    process(page.to_dataframe())
-
-
-
- -
-
-sql_columns(table: str, *, include_system: bool = False) List[Dict[str, Any]]
-

Return a simplified list of SQL-usable columns for a table.

-

Each dict contains name (logical name for SQL), type -(Dataverse attribute type), is_pk (primary key flag), and -label (display name). Virtual columns are always excluded -because the SQL endpoint cannot query them.

-
-
Parameters:
-
    -
  • table (str) – Schema name of the table (e.g. "account").

  • -
  • include_system (bool) – When False (default), columns that end -with common system suffixes (_base, versionnumber, -timezoneruleversionnumber, utcconversiontimezonecode, -importsequencenumber, overriddencreatedon) are excluded.

  • -
-
-
Returns:
-

List of column metadata dicts.

-
-
Return type:
-

list[dict[str, Any]]

-
-
-

Example:

-
cols = client.query.sql_columns("account")
-for c in cols:
-    print(f"{c['name']:30s} {c['type']:20s} PK={c['is_pk']}")
-
-
-
- -
-
-odata_select(table: str, *, include_system: bool = False) List[str]
-

Return a list of column logical names suitable for $select.

-

Can be passed directly to client.records.get(table, select=...).

-
-
Parameters:
-
    -
  • table (str) – Schema name of the table (e.g. "account").

  • -
  • include_system (bool) – Include system columns (default False).

  • -
-
-
Returns:
-

List of lowercase column logical names.

-
-
Return type:
-

list[str]

-
-
-

Example:

-
cols = client.query.odata_select("account")
-for page in client.records.get("account", select=cols, top=10):
-    for r in page:
-        print(r)
-
-
-
- -
-
-odata_expands(table: str) List[Dict[str, Any]]
-

Discover all $expand navigation properties from a table.

-

Returns entries for each outgoing lookup (single-valued navigation -property). Each entry contains the exact PascalCase navigation -property name needed for $expand and @odata.bind, plus -the target entity set name.

-
-
Parameters:
-

table (str) – Schema name of the table (e.g. "contact").

-
-
Returns:
-

List of dicts, each with:

-
    -
  • nav_property – PascalCase navigation property for $expand

  • -
  • target_table – target entity logical name

  • -
  • target_entity_set – target entity set (for @odata.bind)

  • -
  • lookup_attribute – the lookup column logical name

  • -
  • relationship – relationship schema name

  • -
-

-
-
Return type:
-

list[dict[str, Any]]

-
-
-

Example:

-
expands = client.query.odata_expands("contact")
-for e in expands:
-    print(f"expand={e['nav_property']}  -> {e['target_table']}")
-
-# Use in a query
-e = next(e for e in expands if e['target_table'] == 'account')
-for page in client.records.get("contact",
-                               select=["fullname"],
-                               expand=[e['nav_property']]):
-    ...
-
-
-
- -
-
-odata_expand(from_table: str, to_table: str) str
-

Return the navigation property name to $expand from one table to another.

-

Discovers via relationship metadata. Returns the exact PascalCase -string for the expand= parameter.

-
-
Parameters:
-
    -
  • from_table (str) – Schema name of the source table (e.g. "contact").

  • -
  • to_table (str) – Schema name of the target table (e.g. "account").

  • -
-
-
Returns:
-

The navigation property name (PascalCase).

-
-
Return type:
-

str

-
-
Raises:
-

ValueError – If no navigation property found for the target.

-
-
-

Example:

-
nav = client.query.odata_expand("contact", "account")
-# Returns e.g. "parentcustomerid_account"
-for page in client.records.get("contact",
-                               select=["fullname"],
-                               expand=[nav],
-                               top=5):
-    for r in page:
-        acct = r.get(nav) or {}
-        print(f"{r['fullname']} -> {acct.get('name', 'N/A')}")
-
-
-
- -
-
-odata_bind(from_table: str, to_table: str, target_id: str) Dict[str, str]
-

Build an @odata.bind entry for setting a lookup field.

-

Auto-discovers the navigation property name and entity set name -from metadata. Returns a single-entry dict that can be merged -into a create or update payload.

-
-
Parameters:
-
    -
  • from_table (str) – Schema name of the entity being created/updated.

  • -
  • to_table (str) – Schema name of the target entity the lookup points to.

  • -
  • target_id (str) – GUID of the target record.

  • -
-
-
Returns:
-

A dict like {"NavProp@odata.bind": "/entityset(guid)"}.

-
-
Return type:
-

dict[str, str]

-
-
Raises:
-

ValueError – If no relationship found between the tables.

-
-
-

Example:

-
# Instead of manually constructing:
-#   {"parentcustomerid_account@odata.bind": "/accounts(guid)"}
-# Just do:
-bind = client.query.odata_bind("contact", "account", acct_id)
-client.records.create("contact", {
-    "firstname": "Jane",
-    "lastname": "Doe",
-    **bind,
-})
-
-
-
- -
- -
-
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/records/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/records/index.html deleted file mode 100644 index 5c185faf..00000000 --- a/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/records/index.html +++ /dev/null @@ -1,588 +0,0 @@ - - - - - - - - PowerPlatform.Dataverse.operations.records — PowerPlatform-Dataverse-Client 0.1.0b11 documentation - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

PowerPlatform.Dataverse.operations.records

-

Record CRUD operations namespace for the Dataverse SDK.

-
-

Classes

- - - - - - -

RecordOperations

Namespace for record-level CRUD operations.

-
-
-

Module Contents

-
-
-class PowerPlatform.Dataverse.operations.records.RecordOperations(client: PowerPlatform.Dataverse.client.DataverseClient)
-

Namespace for record-level CRUD operations.

-

Accessed via client.records. Provides create, update, delete, and get -operations on individual Dataverse records.

-
-
Parameters:
-

client (DataverseClient) – The parent DataverseClient instance.

-
-
-

Example:

-
client = DataverseClient(base_url, credential)
-
-# Create a single record
-guid = client.records.create("account", {"name": "Contoso Ltd"})
-
-# Get a record
-record = client.records.get("account", guid, select=["name"])
-
-# Update a record
-client.records.update("account", guid, {"telephone1": "555-0100"})
-
-# Delete a record
-client.records.delete("account", guid)
-
-
-
-
-create(table: str, data: Dict[str, Any]) str
-
-create(table: str, data: List[Dict[str, Any]]) List[str]
-

Create one or more records in a Dataverse table.

-

When data is a single dictionary, creates one record and returns its -GUID as a string. When data is a list of dictionaries, creates all -records via the CreateMultiple action and returns a list of GUIDs.

-
-
Parameters:
-
    -
  • table (str) – Schema name of the table (e.g. "account" or "new_MyTestTable").

  • -
  • data (dict or list[dict]) – A single record dictionary or a list of record dictionaries. -Each dictionary maps column schema names to values.

  • -
-
-
Returns:
-

A single GUID string for a single record, or a list of GUID -strings for bulk creation.

-
-
Return type:
-

str or list[str]

-
-
Raises:
-

TypeError – If data is not a dict or list[dict].

-
-
-

Example

-

Create a single record:

-
guid = client.records.create("account", {"name": "Contoso"})
-print(f"Created: {guid}")
-
-
-

Create multiple records:

-
guids = client.records.create("account", [
-    {"name": "Contoso"},
-    {"name": "Fabrikam"},
-])
-print(f"Created {len(guids)} accounts")
-
-
-
- -
-
-update(table: str, ids: str | List[str], changes: Dict[str, Any] | List[Dict[str, Any]]) None
-

Update one or more records in a Dataverse table.

-

Supports three usage patterns:

-
    -
  1. Singleupdate("account", "guid", {"name": "New"})

  2. -
  3. Broadcastupdate("account", [id1, id2], {"status": 1}) -applies the same changes dict to every ID.

  4. -
  5. Pairedupdate("account", [id1, id2], [ch1, ch2]) -applies each changes dict to its corresponding ID (lists must be -equal length).

  6. -
-
-
Parameters:
-
    -
  • table (str) – Schema name of the table (e.g. "account").

  • -
  • ids (str or list[str]) – A single GUID string, or a list of GUID strings.

  • -
  • changes (dict or list[dict]) – A dictionary of field changes (single/broadcast), or a -list of dictionaries (paired, one per ID).

  • -
-
-
Raises:
-

TypeError – If ids is not str or list[str], or if changes -does not match the expected pattern.

-
-
-

Example

-

Single update:

-
client.records.update("account", account_id, {"telephone1": "555-0100"})
-
-
-

Broadcast update:

-
client.records.update("account", [id1, id2], {"statecode": 1})
-
-
-

Paired update:

-
client.records.update(
-    "account",
-    [id1, id2],
-    [{"name": "Name A"}, {"name": "Name B"}],
-)
-
-
-
- -
-
-delete(table: str, ids: str) None
-
-delete(table: str, ids: List[str], *, use_bulk_delete: bool = True) str | None
-

Delete one or more records from a Dataverse table.

-

When ids is a single string, deletes that one record. When ids -is a list, either executes a BulkDelete action (returning the async job -ID) or deletes each record sequentially depending on use_bulk_delete.

-
-
Parameters:
-
    -
  • table (str) – Schema name of the table (e.g. "account").

  • -
  • ids (str or list[str]) – A single GUID string, or a list of GUID strings.

  • -
  • use_bulk_delete (bool) – When True (default) and ids is a list, use -the BulkDelete action and return its async job ID. When False, delete -records one at a time.

  • -
-
-
Returns:
-

The BulkDelete job ID when bulk-deleting; otherwise None.

-
-
Return type:
-

str or None

-
-
Raises:
-

TypeError – If ids is not str or list[str].

-
-
-

Example

-

Delete a single record:

-
client.records.delete("account", account_id)
-
-
-

Bulk delete:

-
job_id = client.records.delete("account", [id1, id2, id3])
-
-
-
- -
-
-get(table: str, record_id: str, *, select: List[str] | None = None) PowerPlatform.Dataverse.models.record.Record
-
-get(table: str, *, select: List[str] | None = None, filter: str | None = None, orderby: List[str] | None = None, top: int | None = None, expand: List[str] | None = None, page_size: int | None = None, count: bool = False, include_annotations: str | None = None) Iterable[List[PowerPlatform.Dataverse.models.record.Record]]
-

Fetch a single record by ID, or fetch multiple records with pagination.

-

This method has two usage patterns:

-

Fetch a single recordget(table, record_id, *, select=...)

-

Pass record_id as a positional argument to retrieve one record -and get back a dict. Query parameters (filter, -orderby, top, expand, page_size) must not be provided.

-

Fetch multiple recordsget(table, *, select=..., filter=..., ...)

-

Omit record_id to perform a paginated fetch and get back a -generator that yields one page (list of record dicts) at a time. -Automatically follows @odata.nextLink for server-side paging.

-
-
Parameters:
-
    -
  • table (str) – Schema name of the table (e.g. "account" or -"new_MyTestTable").

  • -
  • record_id (str or None) – GUID of the record to retrieve. When omitted, -performs a multi-record fetch instead.

  • -
  • select (list[str] or None) – Optional list of column logical names to include. -Column names are automatically lowercased.

  • -
  • filter (str or None) – Optional OData $filter expression (e.g. -"name eq 'Contoso'"). Column names in filter expressions must -use exact lowercase logical names. Only used for multi-record -queries.

  • -
  • orderby (list[str] or None) – Optional list of sort expressions (e.g. -["name asc", "createdon desc"]). Column names are -automatically lowercased. Only used for multi-record queries.

  • -
  • top (int or None) – Optional maximum total number of records to return. Only -used for multi-record queries.

  • -
  • expand (list[str] or None) – Optional list of navigation properties to expand (e.g. -["primarycontactid"]). Case-sensitive; must match -server-defined names exactly. Only used for multi-record queries.

  • -
  • page_size (int or None) – Optional per-page size hint sent via -Prefer: odata.maxpagesize. Only used for multi-record queries.

  • -
  • count (bool) – If True, adds $count=true to include a total -record count in the response. Only used for multi-record queries.

  • -
  • include_annotations (str or None) – OData annotation pattern for the -Prefer: odata.include-annotations header (e.g. "*" or -"OData.Community.Display.V1.FormattedValue"), or None. -Only used for multi-record queries.

  • -
-
-
Returns:
-

A single Record -when record_id is provided, or a generator yielding pages -(lists of Record) -when fetching multiple records.

-
-
Return type:
-

Record or -collections.abc.Iterable[list[Record]]

-
-
Raises:
-
    -
  • TypeError – If record_id is provided but not a string.

  • -
  • ValueError – If query parameters are provided alongside -record_id.

  • -
-
-
-

Example

-

Fetch a single record:

-
record = client.records.get(
-    "account", account_id, select=["name", "telephone1"]
-)
-print(record["name"])
-
-
-

Fetch multiple records with pagination:

-
for page in client.records.get(
-    "account",
-    filter="statecode eq 0",
-    select=["name", "telephone1"],
-    page_size=50,
-):
-    for record in page:
-        print(record["name"])
-
-
-
- -
-
-retrieve(table: str, record_id: str, *, select: List[str] | None = None, expand: List[str] | None = None, include_annotations: str | None = None) PowerPlatform.Dataverse.models.record.Record | None
-

Fetch a single record by its GUID, returning None if not found.

-

GA replacement for records.get(table, record_id). Returns None -instead of raising when the record does not exist (HTTP 404).

-
-
Parameters:
-
    -
  • table (str) – Schema name of the table (e.g. "account").

  • -
  • record_id (str) – GUID of the record to retrieve.

  • -
  • select (list[str] or None) – Optional list of column logical names to include.

  • -
  • expand (list[str] or None) – Optional list of navigation properties to expand (e.g. -["primarycontactid"]). Navigation property names are -case-sensitive and must match the entity’s $metadata.

  • -
  • include_annotations (str or None) – OData annotation pattern for the -Prefer: odata.include-annotations header (e.g. "*" or -"OData.Community.Display.V1.FormattedValue"), or None.

  • -
-
-
Returns:
-

Typed record, or None if not found.

-
-
Return type:
-

Record or None

-
-
-

Example:

-
record = client.records.retrieve(
-    "account", account_id,
-    select=["name", "statuscode"],
-    expand=["primarycontactid"],
-    include_annotations="OData.Community.Display.V1.FormattedValue",
-)
-if record is not None:
-    contact = record.get("primarycontactid") or {}
-    print(contact.get("fullname"))
-
-
-
- -
-
-list(table: str, *, filter: str | PowerPlatform.Dataverse.models.filters.FilterExpression | None = None, select: List[str] | None = None, orderby: List[str] | None = None, top: int | None = None, expand: List[str] | None = None, page_size: int | None = None, count: bool = False, include_annotations: str | None = None) PowerPlatform.Dataverse.models.record.QueryResult
-

Fetch multiple records and return them as a QueryResult.

-

GA replacement for records.get(table, filter=...). All pages are -collected eagerly and returned as a single QueryResult.

-
-
Parameters:
-
    -
  • table (str) – Schema name of the table (e.g. "account").

  • -
  • filter (str or FilterExpression or None) – Optional OData filter string or FilterExpression.

  • -
  • select (list[str] or None) – Optional list of column logical names to include.

  • -
  • orderby (list[str] or None) – Optional list of sort expressions (e.g. ["name asc", "createdon desc"]).

  • -
  • top (int or None) – Maximum total number of records to return.

  • -
  • expand (list[str] or None) – Optional list of navigation properties to expand.

  • -
  • page_size (int or None) – Per-page size hint via Prefer: odata.maxpagesize.

  • -
  • count (bool) – If True, adds $count=true to include a total record count.

  • -
  • include_annotations (str or None) – OData annotation pattern for the -Prefer: odata.include-annotations header, or None.

  • -
-
-
Returns:
-

All matching records collected into a QueryResult.

-
-
Return type:
-

QueryResult

-
-
-

Example:

-
from PowerPlatform.Dataverse import col
-
-result = client.records.list(
-    "account",
-    filter=col("statecode") == 0,
-    select=["name", "statuscode"],
-    orderby=["name asc"],
-    top=100,
-    include_annotations="OData.Community.Display.V1.FormattedValue",
-)
-for record in result:
-    print(record["name"], record.get("statuscode@OData.Community.Display.V1.FormattedValue"))
-
-
-
- -
-
-list_pages(table: str, *, filter: str | PowerPlatform.Dataverse.models.filters.FilterExpression | None = None, select: List[str] | None = None, orderby: List[str] | None = None, top: int | None = None, expand: List[str] | None = None, page_size: int | None = None, count: bool = False, include_annotations: str | None = None) Iterator[PowerPlatform.Dataverse.models.record.QueryResult]
-

Lazily yield one QueryResult per HTTP page.

-

Streaming counterpart to list(). Each iteration triggers one -network request via @odata.nextLink. One-shot — do not iterate -more than once.

-
-
Parameters:
-
    -
  • table (str) – Schema name of the table (e.g. "account").

  • -
  • filter (str or FilterExpression or None) – Optional OData filter string or FilterExpression.

  • -
  • select (list[str] or None) – Optional list of column logical names to include.

  • -
  • orderby (list[str] or None) – Optional list of sort expressions (e.g. ["name asc", "createdon desc"]).

  • -
  • top (int or None) – Maximum total number of records to return.

  • -
  • expand (list[str] or None) – Optional list of navigation properties to expand.

  • -
  • page_size (int or None) – Per-page size hint via Prefer: odata.maxpagesize.

  • -
  • count (bool) – If True, adds $count=true to include a total record count.

  • -
  • include_annotations (str or None) – OData annotation pattern for the -Prefer: odata.include-annotations header, or None.

  • -
-
-
Returns:
-

Iterator of per-page QueryResult objects.

-
-
Return type:
-

Iterator[QueryResult]

-
-
-

Example:

-
for page in client.records.list_pages(
-    "account",
-    filter="statecode eq 0",
-    orderby=["name asc"],
-    page_size=200,
-):
-    process(page.to_dataframe())
-
-
-
- -
-
-upsert(table: str, items: List[PowerPlatform.Dataverse.models.upsert.UpsertItem | Dict[str, Any]]) None
-

Upsert one or more records identified by alternate keys.

-

When items contains a single entry, performs a single upsert via PATCH -using the alternate key in the URL. When items contains multiple entries, -uses the UpsertMultiple bulk action.

-

Each item must be either a UpsertItem -or a plain dict with "alternate_key" and "record" keys (both dicts).

-
-
Parameters:
-
    -
  • table (str) – Schema name of the table (e.g. "account" or "new_MyTestTable").

  • -
  • items (list[UpsertItem | dict]) – Non-empty list of UpsertItem -instances or dicts with "alternate_key" and "record" keys.

  • -
-
-
Returns:
-

None

-
-
Return type:
-

None

-
-
Raises:
-

TypeError – If items is not a non-empty list, or if any element is -neither a UpsertItem nor a -dict with "alternate_key" and "record" keys.

-
-
-

Example

-

Upsert a single record using UpsertItem:

-
from PowerPlatform.Dataverse.models import UpsertItem
-
-client.records.upsert("account", [
-    UpsertItem(
-        alternate_key={"accountnumber": "ACC-001"},
-        record={"name": "Contoso Ltd", "description": "Primary account"},
-    )
-])
-
-
-

Upsert a single record using a plain dict:

-
client.records.upsert("account", [
-    {
-        "alternate_key": {"accountnumber": "ACC-001"},
-        "record": {"name": "Contoso Ltd", "description": "Primary account"},
-    },
-])
-
-
-

Upsert multiple records using UpsertItem:

-
from PowerPlatform.Dataverse.models import UpsertItem
-
-client.records.upsert("account", [
-    UpsertItem(
-        alternate_key={"accountnumber": "ACC-001"},
-        record={"name": "Contoso Ltd", "description": "Primary account"},
-    ),
-    UpsertItem(
-        alternate_key={"accountnumber": "ACC-002"},
-        record={"name": "Fabrikam Inc", "description": "Partner account"},
-    ),
-])
-
-
-

Upsert multiple records using plain dicts:

-
client.records.upsert("account", [
-    {
-        "alternate_key": {"accountnumber": "ACC-001"},
-        "record": {"name": "Contoso Ltd", "description": "Primary account"},
-    },
-    {
-        "alternate_key": {"accountnumber": "ACC-002"},
-        "record": {"name": "Fabrikam Inc", "description": "Partner account"},
-    },
-])
-
-
-

The alternate_key dict may contain multiple columns when the configured -alternate key is composite, e.g. -{"accountnumber": "ACC-001", "address1_postalcode": "98052"}.

-
- -
- -
-
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/tables/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/tables/index.html deleted file mode 100644 index f1dc30b7..00000000 --- a/docs_local/_build/autoapi/PowerPlatform/Dataverse/operations/tables/index.html +++ /dev/null @@ -1,865 +0,0 @@ - - - - - - - - PowerPlatform.Dataverse.operations.tables — PowerPlatform-Dataverse-Client 0.1.0b11 documentation - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

PowerPlatform.Dataverse.operations.tables

-

Table metadata operations namespace for the Dataverse SDK.

-
-

Classes

- - - - - - -

TableOperations

Namespace for table-level metadata operations.

-
-
-

Module Contents

-
-
-class PowerPlatform.Dataverse.operations.tables.TableOperations(client: PowerPlatform.Dataverse.client.DataverseClient)
-

Namespace for table-level metadata operations.

-

Accessed via client.tables. Provides operations to create, delete, -inspect, and list Dataverse tables, as well as add and remove columns.

-
-
Parameters:
-

client (DataverseClient) – The parent DataverseClient instance.

-
-
-

Example:

-
client = DataverseClient(base_url, credential)
-
-# Create a table
-info = client.tables.create(
-    "new_Product",
-    {"new_Price": "decimal", "new_InStock": "bool"},
-    solution="MySolution",
-)
-
-# List tables
-tables = client.tables.list()
-
-# Get table info
-info = client.tables.get("new_Product")
-
-# Add columns
-client.tables.add_columns("new_Product", {"new_Rating": "int"})
-
-# Remove columns
-client.tables.remove_columns("new_Product", "new_Rating")
-
-# Delete a table
-client.tables.delete("new_Product")
-
-
-
-
-create(table: str, columns: Dict[str, Any], *, solution: str | None = None, primary_column: str | None = None, display_name: str | None = None) PowerPlatform.Dataverse.models.table_info.TableInfo
-

Create a custom table with the specified columns.

-
-
Parameters:
-
    -
  • table (str) – Schema name of the table with customization prefix -(e.g. "new_MyTestTable").

  • -
  • columns (dict) – Mapping of column schema names (with customization -prefix) to their types. Supported types include "string" -(or "text"), "memo" (or "multiline"), -"int" (or "integer"), "decimal" -(or "money"), "float" (or "double"), "datetime" -(or "date"), "bool" (or "boolean"), "file", and -Enum subclasses -(for local option sets).

  • -
  • solution (str or None) – Optional solution unique name that should own the new -table. When omitted the table is created in the default solution.

  • -
  • primary_column (str or None) – Optional primary name column schema name with -customization prefix (e.g. "new_ProductName"). If not provided, -defaults to "{prefix}_Name".

  • -
  • display_name (str or None) – Human-readable display name for the table -(e.g. "Product"). When omitted, defaults to the table schema name.

  • -
-
-
Returns:
-

Table metadata with schema_name, entity_set_name, -logical_name, metadata_id, and columns_created. -Supports dict-like access with legacy keys for backward -compatibility.

-
-
Return type:
-

TableInfo

-
-
Raises:
-

MetadataError – If table creation fails or the table already exists.

-
-
-

Example

-

Create a table with simple columns:

-
from enum import IntEnum
-
-class ItemStatus(IntEnum):
-    ACTIVE = 1
-    INACTIVE = 2
-
-result = client.tables.create(
-    "new_Product",
-    {
-        "new_Title": "string",
-        "new_Price": "decimal",
-        "new_Status": ItemStatus,
-    },
-    solution="MySolution",
-    primary_column="new_ProductName",
-    display_name="Product",
-)
-print(f"Created: {result['table_schema_name']}")
-
-
-
- -
-
-delete(table: str) None
-

Delete a custom table by schema name.

-
-
Parameters:
-

table (str) – Schema name of the table (e.g. "new_MyTestTable").

-
-
Raises:
-

MetadataError – If the table does not exist or deletion fails.

-
-
-
-

Warning

-

This operation is irreversible and will delete all records in the -table along with the table definition.

-
-

Example:

-
client.tables.delete("new_MyTestTable")
-
-
-
- -
-
-get(table: str) PowerPlatform.Dataverse.models.table_info.TableInfo | None
-

Get basic metadata for a table if it exists.

-
-
Parameters:
-

table (str) – Schema name of the table (e.g. "new_MyTestTable" -or "account").

-
-
Returns:
-

Table metadata, or None if the table is not found. -Supports dict-like access with legacy keys for backward -compatibility.

-
-
Return type:
-

TableInfo -or None

-
-
-

Example:

-
info = client.tables.get("new_MyTestTable")
-if info:
-    print(f"Logical name: {info['table_logical_name']}")
-    print(f"Entity set: {info['entity_set_name']}")
-
-
-
- -
-
-list(*, filter: str | None = None, select: List[str] | None = None) List[Dict[str, Any]]
-

List all non-private tables in the Dataverse environment.

-

By default returns every table where IsPrivate eq false. Supply -an optional OData $filter expression to further narrow the results. -The expression is combined with the default IsPrivate eq false -clause using and.

-
-
Parameters:
-
    -
  • filter (str or None) – Optional OData $filter expression to further narrow -the list of returned tables (e.g. -"SchemaName eq 'Account'"). Column names in filter -expressions must use the exact property names from the -EntityDefinitions metadata (typically PascalCase).

  • -
  • select (list[str] or None) – Optional list of property names to include in the -response (projected via the OData $select query option). -Property names must use the exact PascalCase names from the -EntityDefinitions metadata (e.g. -["LogicalName", "SchemaName", "DisplayName"]). -When None (the default) or an empty list, all properties are -returned.

  • -
-
-
Returns:
-

List of EntityDefinition metadata dictionaries.

-
-
Return type:
-

list[dict]

-
-
-

Example:

-
# List all non-private tables
-tables = client.tables.list()
-for table in tables:
-    print(table["LogicalName"])
-
-# List only tables whose schema name starts with "new_"
-custom_tables = client.tables.list(
-    filter="startswith(SchemaName, 'new_')"
-)
-
-# List tables with only specific properties
-tables = client.tables.list(
-    select=["LogicalName", "SchemaName", "EntitySetName"]
-)
-
-
-
- -
-
-add_columns(table: str, columns: Dict[str, Any]) List[str]
-

Add one or more columns to an existing table.

-
-
Parameters:
-
    -
  • table (str) – Schema name of the table (e.g. "new_MyTestTable").

  • -
  • columns (dict) – Mapping of column schema names (with customization -prefix) to their types. Supported types are the same as for -create().

  • -
-
-
Returns:
-

Schema names of the columns that were created.

-
-
Return type:
-

list[str]

-
-
Raises:
-

MetadataError – If the table does not exist.

-
-
-

Example:

-
created = client.tables.add_columns(
-    "new_MyTestTable",
-    {"new_Notes": "string", "new_Active": "bool"},
-)
-print(created)  # ['new_Notes', 'new_Active']
-
-
-
- -
-
-remove_columns(table: str, columns: str | List[str]) List[str]
-

Remove one or more columns from a table.

-
-
Parameters:
-
    -
  • table (str) – Schema name of the table (e.g. "new_MyTestTable").

  • -
  • columns (str or list[str]) – Column schema name or list of column schema names to -remove. Must include the customization prefix (e.g. -"new_TestColumn").

  • -
-
-
Returns:
-

Schema names of the columns that were removed.

-
-
Return type:
-

list[str]

-
-
Raises:
-

MetadataError – If the table or a specified column does not exist.

-
-
-

Example:

-
removed = client.tables.remove_columns(
-    "new_MyTestTable",
-    ["new_Notes", "new_Active"],
-)
-print(removed)  # ['new_Notes', 'new_Active']
-
-
-
- -
-
-create_one_to_many_relationship(lookup: PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata, relationship: PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata, *, solution: str | None = None) PowerPlatform.Dataverse.models.relationship.RelationshipInfo
-

Create a one-to-many relationship between tables.

-

This operation creates both the relationship and the lookup attribute -on the referencing table.

-
-
Parameters:
-
-
-
Returns:
-

Relationship metadata with relationship_id, -relationship_schema_name, relationship_type, -lookup_schema_name, referenced_entity, and -referencing_entity.

-
-
Return type:
-

RelationshipInfo

-
-
Raises:
-

HttpError – If the Web API request fails.

-
-
-

Example

-

Create a one-to-many relationship: Department (1) -> Employee (N):

-
from PowerPlatform.Dataverse.models import (
-    LookupAttributeMetadata,
-    OneToManyRelationshipMetadata,
-    Label,
-    LocalizedLabel,
-    CascadeConfiguration,
-)
-from PowerPlatform.Dataverse.common.constants import (
-    CASCADE_BEHAVIOR_REMOVE_LINK,
-)
-
-lookup = LookupAttributeMetadata(
-    schema_name="new_DepartmentId",
-    display_name=Label(
-        localized_labels=[
-            LocalizedLabel(label="Department", language_code=1033)
-        ]
-    ),
-)
-
-relationship = OneToManyRelationshipMetadata(
-    schema_name="new_Department_Employee",
-    referenced_entity="new_department",
-    referencing_entity="new_employee",
-    referenced_attribute="new_departmentid",
-    cascade_configuration=CascadeConfiguration(
-        delete=CASCADE_BEHAVIOR_REMOVE_LINK,
-    ),
-)
-
-result = client.tables.create_one_to_many_relationship(lookup, relationship)
-print(f"Created lookup field: {result.lookup_schema_name}")
-
-
-
- -
-
-create_many_to_many_relationship(relationship: PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata, *, solution: str | None = None) PowerPlatform.Dataverse.models.relationship.RelationshipInfo
-

Create a many-to-many relationship between tables.

-

This operation creates a many-to-many relationship and an intersect -table to manage the relationship.

-
-
Parameters:
-
    -
  • relationship (ManyToManyRelationshipMetadata) – Metadata defining the many-to-many relationship.

  • -
  • solution (str or None) – Optional solution unique name to add relationship to.

  • -
-
-
Returns:
-

Relationship metadata with relationship_id, -relationship_schema_name, relationship_type, -entity1_logical_name, and entity2_logical_name.

-
-
Return type:
-

RelationshipInfo

-
-
Raises:
-

HttpError – If the Web API request fails.

-
-
-

Example

-

Create a many-to-many relationship: Employee <-> Project:

-
from PowerPlatform.Dataverse.models import (
-    ManyToManyRelationshipMetadata,
-)
-
-relationship = ManyToManyRelationshipMetadata(
-    schema_name="new_employee_project",
-    entity1_logical_name="new_employee",
-    entity2_logical_name="new_project",
-)
-
-result = client.tables.create_many_to_many_relationship(relationship)
-print(f"Created: {result.relationship_schema_name}")
-
-
-
- -
-
-delete_relationship(relationship_id: str) None
-

Delete a relationship by its metadata ID.

-
-
Parameters:
-

relationship_id (str) – The GUID of the relationship metadata.

-
-
Raises:
-

HttpError – If the Web API request fails.

-
-
-
-

Warning

-

Deleting a relationship also removes the associated lookup attribute -for one-to-many relationships. This operation is irreversible.

-
-

Example:

-
client.tables.delete_relationship(
-    "12345678-1234-1234-1234-123456789abc"
-)
-
-
-
- -
-
-get_relationship(schema_name: str) PowerPlatform.Dataverse.models.relationship.RelationshipInfo | None
-

Retrieve relationship metadata by schema name.

-
-
Parameters:
-

schema_name (str) – The schema name of the relationship.

-
-
Returns:
-

Relationship metadata, or None if not found.

-
-
Return type:
-

RelationshipInfo -or None

-
-
Raises:
-

HttpError – If the Web API request fails.

-
-
-

Example:

-
rel = client.tables.get_relationship("new_Department_Employee")
-if rel:
-    print(f"Found: {rel.relationship_schema_name}")
-
-
-
- -
-
-create_lookup_field(referencing_table: str, lookup_field_name: str, referenced_table: str, *, display_name: str | None = None, description: str | None = None, required: bool = False, cascade_delete: str = CASCADE_BEHAVIOR_REMOVE_LINK, solution: str | None = None, language_code: int = 1033) PowerPlatform.Dataverse.models.relationship.RelationshipInfo
-

Create a simple lookup field relationship.

-

This is a convenience method that wraps create_one_to_many_relationship() -for the common case of adding a lookup field to an existing table.

-
-
Parameters:
-
    -
  • referencing_table (str) – Logical name of the table that will have -the lookup field (child table).

  • -
  • lookup_field_name (str) – Schema name for the lookup field -(e.g., "new_AccountId").

  • -
  • referenced_table (str) – Logical name of the table being referenced -(parent table).

  • -
  • display_name (str or None) – Display name for the lookup field. Defaults to -the referenced table name.

  • -
  • description (str or None) – Optional description for the lookup field.

  • -
  • required (bool) – Whether the lookup is required. Defaults to False.

  • -
  • cascade_delete (str) – Delete behavior ("RemoveLink", -"Cascade", "Restrict"). Defaults to "RemoveLink".

  • -
  • solution (str or None) – Optional solution unique name to add the relationship -to.

  • -
  • language_code (int) – Language code for labels. Defaults to 1033 -(English).

  • -
-
-
Returns:
-

Relationship metadata with relationship_id, -relationship_schema_name, relationship_type, -lookup_schema_name, referenced_entity, and -referencing_entity.

-
-
Return type:
-

RelationshipInfo

-
-
Raises:
-

HttpError – If the Web API request fails.

-
-
-

Example

-

Create a simple lookup field:

-
result = client.tables.create_lookup_field(
-    referencing_table="new_order",
-    lookup_field_name="new_AccountId",
-    referenced_table="account",
-    display_name="Account",
-    required=True,
-    cascade_delete=CASCADE_BEHAVIOR_REMOVE_LINK,
-)
-print(f"Created lookup: {result['lookup_schema_name']}")
-
-
-
- -
-
-create_alternate_key(table: str, key_name: str, columns: List[str], *, display_name: str | None = None, language_code: int = 1033) PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo
-

Create an alternate key on a table.

-

Alternate keys allow upsert operations to identify records by one or -more columns instead of the primary GUID. After creation the key is -queued for index building; its status will -transition from "Pending" to "Active" once the index is ready.

-
-
Parameters:
-
    -
  • table (str) – Schema name of the table (e.g. "new_Product").

  • -
  • key_name (str) – Schema name for the new alternate key -(e.g. "new_product_code_key").

  • -
  • columns (list[str]) – List of column logical names that compose the key -(e.g. ["new_productcode"]).

  • -
  • display_name (str or None) – Display name for the key. Defaults to -key_name if not provided.

  • -
  • language_code (int) – Language code for labels. Defaults to 1033 -(English).

  • -
-
-
Returns:
-

Metadata for the newly created alternate key.

-
-
Return type:
-

AlternateKeyInfo

-
-
Raises:
-
-
-
-

Example

-

Create a single-column alternate key for upsert:

-
key = client.tables.create_alternate_key(
-    "new_Product",
-    "new_product_code_key",
-    ["new_productcode"],
-    display_name="Product Code",
-)
-print(f"Key ID: {key.metadata_id}")
-print(f"Columns: {key.key_attributes}")
-
-
-
- -
-
-get_alternate_keys(table: str) List[PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo]
-

List all alternate keys defined on a table.

-
-
Parameters:
-

table (str) – Schema name of the table (e.g. "new_Product").

-
-
Returns:
-

List of alternate key metadata objects. May be empty if no -alternate keys are defined.

-
-
Return type:
-

list[AlternateKeyInfo]

-
-
Raises:
-
-
-
-

Example

-

List alternate keys and print their status:

-
keys = client.tables.get_alternate_keys("new_Product")
-for key in keys:
-    print(f"{key.schema_name}: {key.status}")
-
-
-
- -
-
-delete_alternate_key(table: str, key_id: str) None
-

Delete an alternate key by its metadata ID.

-
-
Parameters:
-
    -
  • table (str) – Schema name of the table (e.g. "new_Product").

  • -
  • key_id (str) – Metadata GUID of the alternate key to delete.

  • -
-
-
Raises:
-
-
-
-
-

Warning

-

Deleting an alternate key that is in use by upsert operations will -cause those operations to fail. This operation is irreversible.

-
-

Example:

-
client.tables.delete_alternate_key(
-    "new_Product",
-    "12345678-1234-1234-1234-123456789abc",
-)
-
-
-
- -
-
-list_columns(table: str, *, select: List[str] | None = None, filter: str | None = None) List[Dict[str, Any]]
-

List all attribute (column) definitions for a table.

-
-
Parameters:
-
    -
  • table (str) – Schema name of the table (e.g. "account" or -"new_Product").

  • -
  • select (list[str] or None) – Optional list of property names to project via -$select. Values are passed as-is (PascalCase).

  • -
  • filter (str or None) – Optional OData $filter expression. For example, -"AttributeType eq 'String'" returns only string columns.

  • -
-
-
Returns:
-

List of raw attribute metadata dictionaries.

-
-
Return type:
-

list[dict[str, Any]]

-
-
Raises:
-
-
-
-

Example:

-
# List all columns on the account table
-columns = client.tables.list_columns("account")
-for col in columns:
-    print(f"{col['LogicalName']} ({col.get('AttributeType')})")
-
-# List only specific properties
-columns = client.tables.list_columns(
-    "account",
-    select=["LogicalName", "SchemaName", "AttributeType"],
-)
-
-# Filter to only string attributes
-columns = client.tables.list_columns(
-    "account",
-    filter="AttributeType eq 'String'",
-)
-
-
-
- -
-
-list_relationships(*, filter: str | None = None, select: List[str] | None = None) List[Dict[str, Any]]
-

List all relationship definitions in the environment.

-
-
Parameters:
-
    -
  • filter (str or None) – Optional OData $filter expression. For example, -"RelationshipType eq Microsoft.Dynamics.CRM.RelationshipType'OneToManyRelationship'" -returns only one-to-many relationships.

  • -
  • select (list[str] or None) – Optional list of property names to project via -$select. Values are passed as-is (PascalCase).

  • -
-
-
Returns:
-

List of raw relationship metadata dictionaries.

-
-
Return type:
-

list[dict[str, Any]]

-
-
Raises:
-

HttpError – If the Web API request fails.

-
-
-

Example:

-
# List all relationships
-rels = client.tables.list_relationships()
-for rel in rels:
-    print(f"{rel['SchemaName']} ({rel.get('@odata.type')})")
-
-# Filter by type
-one_to_many = client.tables.list_relationships(
-    filter="RelationshipType eq Microsoft.Dynamics.CRM.RelationshipType'OneToManyRelationship'"
-)
-
-# Select specific properties
-rels = client.tables.list_relationships(
-    select=["SchemaName", "ReferencedEntity", "ReferencingEntity"]
-)
-
-
-
- -
-
-list_table_relationships(table: str, *, filter: str | None = None, select: List[str] | None = None) List[Dict[str, Any]]
-

List all relationships for a specific table.

-

Combines one-to-many, many-to-one, and many-to-many relationships -for the given table by querying -EntityDefinitions({id})/OneToManyRelationships, -EntityDefinitions({id})/ManyToOneRelationships, and -EntityDefinitions({id})/ManyToManyRelationships.

-
-
Parameters:
-
    -
  • table (str) – Schema name of the table (e.g. "account").

  • -
  • filter (str or None) – Optional OData $filter expression applied to each -sub-request.

  • -
  • select (list[str] or None) – Optional list of property names to project via -$select. Values are passed as-is (PascalCase).

  • -
-
-
Returns:
-

Combined list of one-to-many, many-to-one, and many-to-many -relationship metadata dictionaries.

-
-
Return type:
-

list[dict[str, Any]]

-
-
Raises:
-
-
-
-

Example:

-
# List all relationships for the account table
-rels = client.tables.list_table_relationships("account")
-for rel in rels:
-    print(f"{rel['SchemaName']} -> {rel.get('@odata.type')}")
-
-
-
- -
- -
-
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/Dataverse/utils/index.html b/docs_local/_build/autoapi/PowerPlatform/Dataverse/utils/index.html deleted file mode 100644 index 8783fde1..00000000 --- a/docs_local/_build/autoapi/PowerPlatform/Dataverse/utils/index.html +++ /dev/null @@ -1,118 +0,0 @@ - - - - - - - - PowerPlatform.Dataverse.utils — PowerPlatform-Dataverse-Client 0.1.0b11 documentation - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

PowerPlatform.Dataverse.utils

-

Utilities and adapters for the Dataverse SDK.

-

Placeholder module for future utility adapters.

-
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/docs_local/_build/autoapi/PowerPlatform/index.html b/docs_local/_build/autoapi/PowerPlatform/index.html deleted file mode 100644 index 06f01e1f..00000000 --- a/docs_local/_build/autoapi/PowerPlatform/index.html +++ /dev/null @@ -1,122 +0,0 @@ - - - - - - - - PowerPlatform — PowerPlatform-Dataverse-Client 0.1.0b11 documentation - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

PowerPlatform

-
-

Submodules

- -
-
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/docs_local/_build/autoapi/index.html b/docs_local/_build/autoapi/index.html deleted file mode 100644 index 5707b4a9..00000000 --- a/docs_local/_build/autoapi/index.html +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - - - API Reference — PowerPlatform-Dataverse-Client 0.1.0b11 documentation - - - - - - - - - - - - - - - - - - - - -
- - -
-
- - - - - - - \ No newline at end of file diff --git a/docs_local/_build/genindex.html b/docs_local/_build/genindex.html deleted file mode 100644 index 24bcfa08..00000000 --- a/docs_local/_build/genindex.html +++ /dev/null @@ -1,1331 +0,0 @@ - - - - - - - Index — PowerPlatform-Dataverse-Client 0.1.0b11 documentation - - - - - - - - - - - - - - - - - - -
-
-
- - -
- - -

Index

- -
- A - | B - | C - | D - | E - | F - | G - | H - | I - | K - | L - | M - | N - | O - | P - | Q - | R - | S - | T - | U - | V - -
-

A

- - - -
- -

B

- - - -
- -

C

- - - -
- -

D

- - - -
- -

E

- - - -
- -

F

- - - -
- -

G

- - - -
- -

H

- - - -
- -

I

- - - -
- -

K

- - - -
- -

L

- - - -
- -

M

- - -
- -

N

- - - -
- -

O

- - - -
- -

P

- - - -
    -
  • page_size (PowerPlatform.Dataverse.models.query_builder.QueryParams attribute) -
  • -
  • - PowerPlatform - -
  • -
  • - PowerPlatform.Dataverse - -
  • -
  • - PowerPlatform.Dataverse.claude_skill - -
  • -
  • - PowerPlatform.Dataverse.client - -
  • -
  • - PowerPlatform.Dataverse.common - -
  • -
  • - PowerPlatform.Dataverse.common.constants - -
  • -
  • - PowerPlatform.Dataverse.core - -
  • -
  • - PowerPlatform.Dataverse.core.config - -
  • -
  • - PowerPlatform.Dataverse.core.errors - -
  • -
  • - PowerPlatform.Dataverse.core.log_config - -
  • -
  • - PowerPlatform.Dataverse.data - -
  • -
  • - PowerPlatform.Dataverse.extensions - -
  • -
  • - PowerPlatform.Dataverse.migration - -
  • -
  • - PowerPlatform.Dataverse.migration.migrate_v0_to_v1 - -
  • -
  • - PowerPlatform.Dataverse.models - -
  • -
  • - PowerPlatform.Dataverse.models.batch - -
  • -
  • - PowerPlatform.Dataverse.models.fetchxml_query - -
  • -
- -

Q

- - - -
- -

R

- - - -
- -

S

- - - -
- -

T

- - - -
- -

U

- - - -
- -

V

- - - -
- - - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/docs_local/_build/index.html b/docs_local/_build/index.html deleted file mode 100644 index f28a35e0..00000000 --- a/docs_local/_build/index.html +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - - - PowerPlatform Dataverse Client SDK — PowerPlatform-Dataverse-Client 0.1.0b11 documentation - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

PowerPlatform Dataverse Client SDK

-
-

Contents:

- -
-
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/docs_local/_build/objects.inv b/docs_local/_build/objects.inv deleted file mode 100644 index 56d18ab10ee804d18f7f6cab46a4c0047dad9f16..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3506 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 g-LK@5mAle{V4f~_1 - - - - - - Python Module Index — PowerPlatform-Dataverse-Client 0.1.0b11 documentation - - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- - -

Python Module Index

- -
- p -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
- p
- PowerPlatform -
    - PowerPlatform.Dataverse -
    - PowerPlatform.Dataverse.claude_skill -
    - PowerPlatform.Dataverse.client -
    - PowerPlatform.Dataverse.common -
    - PowerPlatform.Dataverse.common.constants -
    - PowerPlatform.Dataverse.core -
    - PowerPlatform.Dataverse.core.config -
    - PowerPlatform.Dataverse.core.errors -
    - PowerPlatform.Dataverse.core.log_config -
    - PowerPlatform.Dataverse.data -
    - PowerPlatform.Dataverse.extensions -
    - PowerPlatform.Dataverse.migration -
    - PowerPlatform.Dataverse.migration.migrate_v0_to_v1 -
    - PowerPlatform.Dataverse.models -
    - PowerPlatform.Dataverse.models.batch -
    - PowerPlatform.Dataverse.models.fetchxml_query -
    - PowerPlatform.Dataverse.models.filters -
    - PowerPlatform.Dataverse.models.labels -
    - PowerPlatform.Dataverse.models.protocol -
    - PowerPlatform.Dataverse.models.query_builder -
    - PowerPlatform.Dataverse.models.record -
    - PowerPlatform.Dataverse.models.relationship -
    - PowerPlatform.Dataverse.models.table_info -
    - PowerPlatform.Dataverse.models.upsert -
    - PowerPlatform.Dataverse.operations -
    - PowerPlatform.Dataverse.operations.batch -
    - PowerPlatform.Dataverse.operations.dataframe -
    - PowerPlatform.Dataverse.operations.files -
    - PowerPlatform.Dataverse.operations.query -
    - PowerPlatform.Dataverse.operations.records -
    - PowerPlatform.Dataverse.operations.tables -
    - PowerPlatform.Dataverse.utils -
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/docs_local/_build/search.html b/docs_local/_build/search.html deleted file mode 100644 index 36557a12..00000000 --- a/docs_local/_build/search.html +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - - Search — PowerPlatform-Dataverse-Client 0.1.0b11 documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -

Search

- - - - -

- Searching for multiple words only shows matches that contain - all words. -

- - -
- - - -
- - -
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/docs_local/_build/searchindex.js b/docs_local/_build/searchindex.js deleted file mode 100644 index 21679509..00000000 --- a/docs_local/_build/searchindex.js +++ /dev/null @@ -1 +0,0 @@ -Search.setIndex({"alltitles":{"API Reference":[[33,null]],"Attributes":[[2,"attributes"]],"Classes":[[1,"classes"],[4,"classes"],[7,"classes"],[13,"classes"],[14,"classes"],[15,"classes"],[17,"classes"],[18,"classes"],[19,"classes"],[20,"classes"],[21,"classes"],[22,"classes"],[23,"classes"],[24,"classes"],[25,"classes"],[26,"classes"],[28,"classes"],[29,"classes"],[30,"classes"]],"Contents:":[[34,null]],"Exceptions":[[5,"exceptions"]],"Functions":[[12,"functions"],[15,"functions"]],"Module Contents":[[1,"module-contents"],[2,"module-contents"],[4,"module-contents"],[5,"module-contents"],[7,"module-contents"],[12,"module-contents"],[13,"module-contents"],[14,"module-contents"],[15,"module-contents"],[17,"module-contents"],[18,"module-contents"],[19,"module-contents"],[20,"module-contents"],[21,"module-contents"],[22,"module-contents"],[23,"module-contents"],[24,"module-contents"],[25,"module-contents"],[26,"module-contents"],[28,"module-contents"],[29,"module-contents"],[30,"module-contents"]],"PowerPlatform":[[32,null]],"PowerPlatform Dataverse Client SDK":[[34,null]],"PowerPlatform.Dataverse":[[10,null]],"PowerPlatform.Dataverse.claude_skill":[[0,null]],"PowerPlatform.Dataverse.client":[[1,null]],"PowerPlatform.Dataverse.common":[[3,null]],"PowerPlatform.Dataverse.common.constants":[[2,null]],"PowerPlatform.Dataverse.core":[[6,null]],"PowerPlatform.Dataverse.core.config":[[4,null]],"PowerPlatform.Dataverse.core.errors":[[5,null]],"PowerPlatform.Dataverse.core.log_config":[[7,null]],"PowerPlatform.Dataverse.data":[[8,null]],"PowerPlatform.Dataverse.extensions":[[9,null]],"PowerPlatform.Dataverse.migration":[[11,null]],"PowerPlatform.Dataverse.migration.migrate_v0_to_v1":[[12,null]],"PowerPlatform.Dataverse.models":[[16,null]],"PowerPlatform.Dataverse.models.batch":[[13,null]],"PowerPlatform.Dataverse.models.fetchxml_query":[[14,null]],"PowerPlatform.Dataverse.models.filters":[[15,null]],"PowerPlatform.Dataverse.models.labels":[[17,null]],"PowerPlatform.Dataverse.models.protocol":[[18,null]],"PowerPlatform.Dataverse.models.query_builder":[[19,null]],"PowerPlatform.Dataverse.models.record":[[20,null]],"PowerPlatform.Dataverse.models.relationship":[[21,null]],"PowerPlatform.Dataverse.models.table_info":[[22,null]],"PowerPlatform.Dataverse.models.upsert":[[23,null]],"PowerPlatform.Dataverse.operations":[[27,null]],"PowerPlatform.Dataverse.operations.batch":[[24,null]],"PowerPlatform.Dataverse.operations.dataframe":[[25,null]],"PowerPlatform.Dataverse.operations.files":[[26,null]],"PowerPlatform.Dataverse.operations.query":[[28,null]],"PowerPlatform.Dataverse.operations.records":[[29,null]],"PowerPlatform.Dataverse.operations.tables":[[30,null]],"PowerPlatform.Dataverse.utils":[[31,null]],"Submodules":[[3,"submodules"],[6,"submodules"],[10,"submodules"],[11,"submodules"],[16,"submodules"],[27,"submodules"],[32,"submodules"]],"Transformations applied":[[12,"transformations-applied"]]},"docnames":["autoapi/PowerPlatform/Dataverse/claude_skill/index","autoapi/PowerPlatform/Dataverse/client/index","autoapi/PowerPlatform/Dataverse/common/constants/index","autoapi/PowerPlatform/Dataverse/common/index","autoapi/PowerPlatform/Dataverse/core/config/index","autoapi/PowerPlatform/Dataverse/core/errors/index","autoapi/PowerPlatform/Dataverse/core/index","autoapi/PowerPlatform/Dataverse/core/log_config/index","autoapi/PowerPlatform/Dataverse/data/index","autoapi/PowerPlatform/Dataverse/extensions/index","autoapi/PowerPlatform/Dataverse/index","autoapi/PowerPlatform/Dataverse/migration/index","autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index","autoapi/PowerPlatform/Dataverse/models/batch/index","autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index","autoapi/PowerPlatform/Dataverse/models/filters/index","autoapi/PowerPlatform/Dataverse/models/index","autoapi/PowerPlatform/Dataverse/models/labels/index","autoapi/PowerPlatform/Dataverse/models/protocol/index","autoapi/PowerPlatform/Dataverse/models/query_builder/index","autoapi/PowerPlatform/Dataverse/models/record/index","autoapi/PowerPlatform/Dataverse/models/relationship/index","autoapi/PowerPlatform/Dataverse/models/table_info/index","autoapi/PowerPlatform/Dataverse/models/upsert/index","autoapi/PowerPlatform/Dataverse/operations/batch/index","autoapi/PowerPlatform/Dataverse/operations/dataframe/index","autoapi/PowerPlatform/Dataverse/operations/files/index","autoapi/PowerPlatform/Dataverse/operations/index","autoapi/PowerPlatform/Dataverse/operations/query/index","autoapi/PowerPlatform/Dataverse/operations/records/index","autoapi/PowerPlatform/Dataverse/operations/tables/index","autoapi/PowerPlatform/Dataverse/utils/index","autoapi/PowerPlatform/index","autoapi/index","index"],"envversion":{"sphinx":66,"sphinx.domains.c":3,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":9,"sphinx.domains.index":1,"sphinx.domains.javascript":3,"sphinx.domains.math":2,"sphinx.domains.python":4,"sphinx.domains.rst":2,"sphinx.domains.std":2},"filenames":["autoapi\\PowerPlatform\\Dataverse\\claude_skill\\index.rst","autoapi\\PowerPlatform\\Dataverse\\client\\index.rst","autoapi\\PowerPlatform\\Dataverse\\common\\constants\\index.rst","autoapi\\PowerPlatform\\Dataverse\\common\\index.rst","autoapi\\PowerPlatform\\Dataverse\\core\\config\\index.rst","autoapi\\PowerPlatform\\Dataverse\\core\\errors\\index.rst","autoapi\\PowerPlatform\\Dataverse\\core\\index.rst","autoapi\\PowerPlatform\\Dataverse\\core\\log_config\\index.rst","autoapi\\PowerPlatform\\Dataverse\\data\\index.rst","autoapi\\PowerPlatform\\Dataverse\\extensions\\index.rst","autoapi\\PowerPlatform\\Dataverse\\index.rst","autoapi\\PowerPlatform\\Dataverse\\migration\\index.rst","autoapi\\PowerPlatform\\Dataverse\\migration\\migrate_v0_to_v1\\index.rst","autoapi\\PowerPlatform\\Dataverse\\models\\batch\\index.rst","autoapi\\PowerPlatform\\Dataverse\\models\\fetchxml_query\\index.rst","autoapi\\PowerPlatform\\Dataverse\\models\\filters\\index.rst","autoapi\\PowerPlatform\\Dataverse\\models\\index.rst","autoapi\\PowerPlatform\\Dataverse\\models\\labels\\index.rst","autoapi\\PowerPlatform\\Dataverse\\models\\protocol\\index.rst","autoapi\\PowerPlatform\\Dataverse\\models\\query_builder\\index.rst","autoapi\\PowerPlatform\\Dataverse\\models\\record\\index.rst","autoapi\\PowerPlatform\\Dataverse\\models\\relationship\\index.rst","autoapi\\PowerPlatform\\Dataverse\\models\\table_info\\index.rst","autoapi\\PowerPlatform\\Dataverse\\models\\upsert\\index.rst","autoapi\\PowerPlatform\\Dataverse\\operations\\batch\\index.rst","autoapi\\PowerPlatform\\Dataverse\\operations\\dataframe\\index.rst","autoapi\\PowerPlatform\\Dataverse\\operations\\files\\index.rst","autoapi\\PowerPlatform\\Dataverse\\operations\\index.rst","autoapi\\PowerPlatform\\Dataverse\\operations\\query\\index.rst","autoapi\\PowerPlatform\\Dataverse\\operations\\records\\index.rst","autoapi\\PowerPlatform\\Dataverse\\operations\\tables\\index.rst","autoapi\\PowerPlatform\\Dataverse\\utils\\index.rst","autoapi\\PowerPlatform\\index.rst","autoapi\\index.rst","index.rst"],"indexentries":{"add_columns() (powerplatform.dataverse.operations.batch.batchtableoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchTableOperations.add_columns",false]],"add_columns() (powerplatform.dataverse.operations.tables.tableoperations method)":[[30,"PowerPlatform.Dataverse.operations.tables.TableOperations.add_columns",false]],"additional_properties (powerplatform.dataverse.models.labels.label attribute)":[[17,"PowerPlatform.Dataverse.models.labels.Label.additional_properties",false]],"additional_properties (powerplatform.dataverse.models.labels.localizedlabel attribute)":[[17,"PowerPlatform.Dataverse.models.labels.LocalizedLabel.additional_properties",false]],"additional_properties (powerplatform.dataverse.models.relationship.cascadeconfiguration attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.CascadeConfiguration.additional_properties",false]],"additional_properties (powerplatform.dataverse.models.relationship.lookupattributemetadata attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata.additional_properties",false]],"additional_properties (powerplatform.dataverse.models.relationship.manytomanyrelationshipmetadata attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata.additional_properties",false]],"additional_properties (powerplatform.dataverse.models.relationship.onetomanyrelationshipmetadata attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata.additional_properties",false]],"alternate_key (powerplatform.dataverse.models.upsert.upsertitem attribute)":[[23,"PowerPlatform.Dataverse.models.upsert.UpsertItem.alternate_key",false]],"alternatekeyinfo (class in powerplatform.dataverse.models.table_info)":[[22,"PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo",false]],"assign (powerplatform.dataverse.models.relationship.cascadeconfiguration attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.CascadeConfiguration.assign",false]],"auth (powerplatform.dataverse.client.dataverseclient attribute)":[[1,"PowerPlatform.Dataverse.client.DataverseClient.auth",false]],"backup_count (powerplatform.dataverse.core.log_config.logconfig attribute)":[[7,"PowerPlatform.Dataverse.core.log_config.LogConfig.backup_count",false]],"batch (powerplatform.dataverse.client.dataverseclient attribute)":[[1,"PowerPlatform.Dataverse.client.DataverseClient.batch",false]],"batchdataframeoperations (class in powerplatform.dataverse.operations.batch)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchDataFrameOperations",false]],"batchitemresponse (class in powerplatform.dataverse.models.batch)":[[13,"PowerPlatform.Dataverse.models.batch.BatchItemResponse",false]],"batchoperations (class in powerplatform.dataverse.operations.batch)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchOperations",false]],"batchqueryoperations (class in powerplatform.dataverse.operations.batch)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchQueryOperations",false]],"batchrecordoperations (class in powerplatform.dataverse.operations.batch)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchRecordOperations",false]],"batchrequest (class in powerplatform.dataverse.operations.batch)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchRequest",false]],"batchresult (class in powerplatform.dataverse.models.batch)":[[13,"PowerPlatform.Dataverse.models.batch.BatchResult",false]],"batchtableoperations (class in powerplatform.dataverse.operations.batch)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchTableOperations",false]],"between() (in module powerplatform.dataverse.models.filters)":[[15,"PowerPlatform.Dataverse.models.filters.between",false]],"between() (powerplatform.dataverse.models.filters.columnproxy method)":[[15,"PowerPlatform.Dataverse.models.filters.ColumnProxy.between",false]],"builder() (powerplatform.dataverse.operations.query.queryoperations method)":[[28,"PowerPlatform.Dataverse.operations.query.QueryOperations.builder",false]],"cascade_behavior_cascade (in module powerplatform.dataverse.common.constants)":[[2,"PowerPlatform.Dataverse.common.constants.CASCADE_BEHAVIOR_CASCADE",false]],"cascade_behavior_no_cascade (in module powerplatform.dataverse.common.constants)":[[2,"PowerPlatform.Dataverse.common.constants.CASCADE_BEHAVIOR_NO_CASCADE",false]],"cascade_behavior_remove_link (in module powerplatform.dataverse.common.constants)":[[2,"PowerPlatform.Dataverse.common.constants.CASCADE_BEHAVIOR_REMOVE_LINK",false]],"cascade_behavior_restrict (in module powerplatform.dataverse.common.constants)":[[2,"PowerPlatform.Dataverse.common.constants.CASCADE_BEHAVIOR_RESTRICT",false]],"cascade_configuration (powerplatform.dataverse.models.relationship.onetomanyrelationshipmetadata attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata.cascade_configuration",false]],"cascadeconfiguration (class in powerplatform.dataverse.models.relationship)":[[21,"PowerPlatform.Dataverse.models.relationship.CascadeConfiguration",false]],"changeset (class in powerplatform.dataverse.operations.batch)":[[24,"PowerPlatform.Dataverse.operations.batch.ChangeSet",false]],"changeset() (powerplatform.dataverse.operations.batch.batchrequest method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchRequest.changeset",false]],"changesetrecordoperations (class in powerplatform.dataverse.operations.batch)":[[24,"PowerPlatform.Dataverse.operations.batch.ChangeSetRecordOperations",false]],"close() (powerplatform.dataverse.client.dataverseclient method)":[[1,"PowerPlatform.Dataverse.client.DataverseClient.close",false]],"code (powerplatform.dataverse.core.errors.dataverseerror attribute)":[[5,"PowerPlatform.Dataverse.core.errors.DataverseError.code",false]],"col() (in module powerplatform.dataverse.models.filters)":[[15,"PowerPlatform.Dataverse.models.filters.col",false]],"columninfo (class in powerplatform.dataverse.models.table_info)":[[22,"PowerPlatform.Dataverse.models.table_info.ColumnInfo",false]],"columnproxy (class in powerplatform.dataverse.models.filters)":[[15,"PowerPlatform.Dataverse.models.filters.ColumnProxy",false]],"columns (powerplatform.dataverse.models.table_info.tableinfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.TableInfo.columns",false]],"columns_created (powerplatform.dataverse.models.table_info.tableinfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.TableInfo.columns_created",false]],"contains() (in module powerplatform.dataverse.models.filters)":[[15,"PowerPlatform.Dataverse.models.filters.contains",false]],"contains() (powerplatform.dataverse.models.filters.columnproxy method)":[[15,"PowerPlatform.Dataverse.models.filters.ColumnProxy.contains",false]],"content_id (powerplatform.dataverse.models.batch.batchitemresponse attribute)":[[13,"PowerPlatform.Dataverse.models.batch.BatchItemResponse.content_id",false]],"count (powerplatform.dataverse.models.query_builder.queryparams attribute)":[[19,"PowerPlatform.Dataverse.models.query_builder.QueryParams.count",false]],"create() (powerplatform.dataverse.operations.batch.batchdataframeoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchDataFrameOperations.create",false]],"create() (powerplatform.dataverse.operations.batch.batchrecordoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchRecordOperations.create",false]],"create() (powerplatform.dataverse.operations.batch.batchtableoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchTableOperations.create",false]],"create() (powerplatform.dataverse.operations.batch.changesetrecordoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.ChangeSetRecordOperations.create",false]],"create() (powerplatform.dataverse.operations.dataframe.dataframeoperations method)":[[25,"PowerPlatform.Dataverse.operations.dataframe.DataFrameOperations.create",false]],"create() (powerplatform.dataverse.operations.records.recordoperations method)":[[29,"PowerPlatform.Dataverse.operations.records.RecordOperations.create",false]],"create() (powerplatform.dataverse.operations.tables.tableoperations method)":[[30,"PowerPlatform.Dataverse.operations.tables.TableOperations.create",false]],"create_alternate_key() (powerplatform.dataverse.operations.tables.tableoperations method)":[[30,"PowerPlatform.Dataverse.operations.tables.TableOperations.create_alternate_key",false]],"create_lookup_field() (powerplatform.dataverse.operations.batch.batchtableoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchTableOperations.create_lookup_field",false]],"create_lookup_field() (powerplatform.dataverse.operations.tables.tableoperations method)":[[30,"PowerPlatform.Dataverse.operations.tables.TableOperations.create_lookup_field",false]],"create_many_to_many_relationship() (powerplatform.dataverse.operations.batch.batchtableoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchTableOperations.create_many_to_many_relationship",false]],"create_many_to_many_relationship() (powerplatform.dataverse.operations.tables.tableoperations method)":[[30,"PowerPlatform.Dataverse.operations.tables.TableOperations.create_many_to_many_relationship",false]],"create_one_to_many_relationship() (powerplatform.dataverse.operations.batch.batchtableoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchTableOperations.create_one_to_many_relationship",false]],"create_one_to_many_relationship() (powerplatform.dataverse.operations.tables.tableoperations method)":[[30,"PowerPlatform.Dataverse.operations.tables.TableOperations.create_one_to_many_relationship",false]],"data (powerplatform.dataverse.models.batch.batchitemresponse attribute)":[[13,"PowerPlatform.Dataverse.models.batch.BatchItemResponse.data",false]],"data (powerplatform.dataverse.models.record.record attribute)":[[20,"PowerPlatform.Dataverse.models.record.Record.data",false]],"dataframe (powerplatform.dataverse.client.dataverseclient attribute)":[[1,"PowerPlatform.Dataverse.client.DataverseClient.dataframe",false]],"dataframe (powerplatform.dataverse.operations.batch.batchrequest attribute)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchRequest.dataframe",false]],"dataframeoperations (class in powerplatform.dataverse.operations.dataframe)":[[25,"PowerPlatform.Dataverse.operations.dataframe.DataFrameOperations",false]],"dataverseclient (class in powerplatform.dataverse.client)":[[1,"PowerPlatform.Dataverse.client.DataverseClient",false]],"dataverseconfig (class in powerplatform.dataverse.core.config)":[[4,"PowerPlatform.Dataverse.core.config.DataverseConfig",false]],"dataverseerror":[[5,"PowerPlatform.Dataverse.core.errors.DataverseError",false]],"dataversemodel (class in powerplatform.dataverse.models.protocol)":[[18,"PowerPlatform.Dataverse.models.protocol.DataverseModel",false]],"delete (powerplatform.dataverse.models.relationship.cascadeconfiguration attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.CascadeConfiguration.delete",false]],"delete() (powerplatform.dataverse.operations.batch.batchdataframeoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchDataFrameOperations.delete",false]],"delete() (powerplatform.dataverse.operations.batch.batchrecordoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchRecordOperations.delete",false]],"delete() (powerplatform.dataverse.operations.batch.batchtableoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchTableOperations.delete",false]],"delete() (powerplatform.dataverse.operations.batch.changesetrecordoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.ChangeSetRecordOperations.delete",false]],"delete() (powerplatform.dataverse.operations.dataframe.dataframeoperations method)":[[25,"PowerPlatform.Dataverse.operations.dataframe.DataFrameOperations.delete",false]],"delete() (powerplatform.dataverse.operations.records.recordoperations method)":[[29,"PowerPlatform.Dataverse.operations.records.RecordOperations.delete",false]],"delete() (powerplatform.dataverse.operations.tables.tableoperations method)":[[30,"PowerPlatform.Dataverse.operations.tables.TableOperations.delete",false]],"delete_alternate_key() (powerplatform.dataverse.operations.tables.tableoperations method)":[[30,"PowerPlatform.Dataverse.operations.tables.TableOperations.delete_alternate_key",false]],"delete_relationship() (powerplatform.dataverse.operations.batch.batchtableoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchTableOperations.delete_relationship",false]],"delete_relationship() (powerplatform.dataverse.operations.tables.tableoperations method)":[[30,"PowerPlatform.Dataverse.operations.tables.TableOperations.delete_relationship",false]],"description (powerplatform.dataverse.models.relationship.lookupattributemetadata attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata.description",false]],"description (powerplatform.dataverse.models.table_info.columninfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.ColumnInfo.description",false]],"description (powerplatform.dataverse.models.table_info.tableinfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.TableInfo.description",false]],"details (powerplatform.dataverse.core.errors.dataverseerror attribute)":[[5,"PowerPlatform.Dataverse.core.errors.DataverseError.details",false]],"display_name (powerplatform.dataverse.models.relationship.lookupattributemetadata attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata.display_name",false]],"display_name (powerplatform.dataverse.models.table_info.columninfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.ColumnInfo.display_name",false]],"display_name (powerplatform.dataverse.models.table_info.tableinfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.TableInfo.display_name",false]],"endswith() (in module powerplatform.dataverse.models.filters)":[[15,"PowerPlatform.Dataverse.models.filters.endswith",false]],"endswith() (powerplatform.dataverse.models.filters.columnproxy method)":[[15,"PowerPlatform.Dataverse.models.filters.ColumnProxy.endswith",false]],"entity1_logical_name (powerplatform.dataverse.models.relationship.manytomanyrelationshipmetadata attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata.entity1_logical_name",false]],"entity1_logical_name (powerplatform.dataverse.models.relationship.relationshipinfo attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.RelationshipInfo.entity1_logical_name",false]],"entity2_logical_name (powerplatform.dataverse.models.relationship.manytomanyrelationshipmetadata attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata.entity2_logical_name",false]],"entity2_logical_name (powerplatform.dataverse.models.relationship.relationshipinfo attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.RelationshipInfo.entity2_logical_name",false]],"entity_id (powerplatform.dataverse.models.batch.batchitemresponse attribute)":[[13,"PowerPlatform.Dataverse.models.batch.BatchItemResponse.entity_id",false]],"entity_ids (powerplatform.dataverse.models.batch.batchresult property)":[[13,"PowerPlatform.Dataverse.models.batch.BatchResult.entity_ids",false]],"entity_set_name (powerplatform.dataverse.models.table_info.tableinfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.TableInfo.entity_set_name",false]],"eq() (in module powerplatform.dataverse.models.filters)":[[15,"PowerPlatform.Dataverse.models.filters.eq",false]],"error_code (powerplatform.dataverse.models.batch.batchitemresponse attribute)":[[13,"PowerPlatform.Dataverse.models.batch.BatchItemResponse.error_code",false]],"error_message (powerplatform.dataverse.models.batch.batchitemresponse attribute)":[[13,"PowerPlatform.Dataverse.models.batch.BatchItemResponse.error_message",false]],"etag (powerplatform.dataverse.models.record.record attribute)":[[20,"PowerPlatform.Dataverse.models.record.Record.etag",false]],"execute() (powerplatform.dataverse.models.fetchxml_query.fetchxmlquery method)":[[14,"PowerPlatform.Dataverse.models.fetchxml_query.FetchXmlQuery.execute",false]],"execute() (powerplatform.dataverse.models.query_builder.querybuilder method)":[[19,"PowerPlatform.Dataverse.models.query_builder.QueryBuilder.execute",false]],"execute() (powerplatform.dataverse.operations.batch.batchrequest method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchRequest.execute",false]],"execute_pages() (powerplatform.dataverse.models.fetchxml_query.fetchxmlquery method)":[[14,"PowerPlatform.Dataverse.models.fetchxml_query.FetchXmlQuery.execute_pages",false]],"execute_pages() (powerplatform.dataverse.models.query_builder.querybuilder method)":[[19,"PowerPlatform.Dataverse.models.query_builder.QueryBuilder.execute_pages",false]],"expand (powerplatform.dataverse.models.query_builder.queryparams attribute)":[[19,"PowerPlatform.Dataverse.models.query_builder.QueryParams.expand",false]],"expandoption (class in powerplatform.dataverse.models.query_builder)":[[19,"PowerPlatform.Dataverse.models.query_builder.ExpandOption",false]],"failed (powerplatform.dataverse.models.batch.batchresult property)":[[13,"PowerPlatform.Dataverse.models.batch.BatchResult.failed",false]],"fetchxml() (powerplatform.dataverse.operations.query.queryoperations method)":[[28,"PowerPlatform.Dataverse.operations.query.QueryOperations.fetchxml",false]],"fetchxmlquery (class in powerplatform.dataverse.models.fetchxml_query)":[[14,"PowerPlatform.Dataverse.models.fetchxml_query.FetchXmlQuery",false]],"fileoperations (class in powerplatform.dataverse.operations.files)":[[26,"PowerPlatform.Dataverse.operations.files.FileOperations",false]],"files (powerplatform.dataverse.client.dataverseclient attribute)":[[1,"PowerPlatform.Dataverse.client.DataverseClient.files",false]],"filter (powerplatform.dataverse.models.query_builder.queryparams attribute)":[[19,"PowerPlatform.Dataverse.models.query_builder.QueryParams.filter",false]],"filter() (powerplatform.dataverse.models.query_builder.expandoption method)":[[19,"PowerPlatform.Dataverse.models.query_builder.ExpandOption.filter",false]],"filter_in() (in module powerplatform.dataverse.models.filters)":[[15,"PowerPlatform.Dataverse.models.filters.filter_in",false]],"filterexpression (class in powerplatform.dataverse.models.filters)":[[15,"PowerPlatform.Dataverse.models.filters.FilterExpression",false]],"find_manual_patterns() (in module powerplatform.dataverse.migration.migrate_v0_to_v1)":[[12,"PowerPlatform.Dataverse.migration.migrate_v0_to_v1.find_manual_patterns",false]],"first() (powerplatform.dataverse.models.record.queryresult method)":[[20,"PowerPlatform.Dataverse.models.record.QueryResult.first",false]],"flush_cache() (powerplatform.dataverse.client.dataverseclient method)":[[1,"PowerPlatform.Dataverse.client.DataverseClient.flush_cache",false]],"from_api_response() (powerplatform.dataverse.models.record.record class method)":[[20,"PowerPlatform.Dataverse.models.record.Record.from_api_response",false]],"from_api_response() (powerplatform.dataverse.models.relationship.relationshipinfo class method)":[[21,"PowerPlatform.Dataverse.models.relationship.RelationshipInfo.from_api_response",false]],"from_api_response() (powerplatform.dataverse.models.table_info.alternatekeyinfo class method)":[[22,"PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo.from_api_response",false]],"from_api_response() (powerplatform.dataverse.models.table_info.columninfo class method)":[[22,"PowerPlatform.Dataverse.models.table_info.ColumnInfo.from_api_response",false]],"from_api_response() (powerplatform.dataverse.models.table_info.tableinfo class method)":[[22,"PowerPlatform.Dataverse.models.table_info.TableInfo.from_api_response",false]],"from_dict() (powerplatform.dataverse.models.protocol.dataversemodel class method)":[[18,"PowerPlatform.Dataverse.models.protocol.DataverseModel.from_dict",false]],"from_dict() (powerplatform.dataverse.models.table_info.tableinfo class method)":[[22,"PowerPlatform.Dataverse.models.table_info.TableInfo.from_dict",false]],"from_env() (powerplatform.dataverse.core.config.dataverseconfig class method)":[[4,"PowerPlatform.Dataverse.core.config.DataverseConfig.from_env",false]],"from_many_to_many() (powerplatform.dataverse.models.relationship.relationshipinfo class method)":[[21,"PowerPlatform.Dataverse.models.relationship.RelationshipInfo.from_many_to_many",false]],"from_one_to_many() (powerplatform.dataverse.models.relationship.relationshipinfo class method)":[[21,"PowerPlatform.Dataverse.models.relationship.RelationshipInfo.from_one_to_many",false]],"ge() (in module powerplatform.dataverse.models.filters)":[[15,"PowerPlatform.Dataverse.models.filters.ge",false]],"get() (powerplatform.dataverse.models.record.record method)":[[20,"PowerPlatform.Dataverse.models.record.Record.get",false]],"get() (powerplatform.dataverse.models.table_info.tableinfo method)":[[22,"PowerPlatform.Dataverse.models.table_info.TableInfo.get",false]],"get() (powerplatform.dataverse.operations.batch.batchrecordoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchRecordOperations.get",false]],"get() (powerplatform.dataverse.operations.batch.batchtableoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchTableOperations.get",false]],"get() (powerplatform.dataverse.operations.dataframe.dataframeoperations method)":[[25,"PowerPlatform.Dataverse.operations.dataframe.DataFrameOperations.get",false]],"get() (powerplatform.dataverse.operations.records.recordoperations method)":[[29,"PowerPlatform.Dataverse.operations.records.RecordOperations.get",false]],"get() (powerplatform.dataverse.operations.tables.tableoperations method)":[[30,"PowerPlatform.Dataverse.operations.tables.TableOperations.get",false]],"get_alternate_keys() (powerplatform.dataverse.operations.tables.tableoperations method)":[[30,"PowerPlatform.Dataverse.operations.tables.TableOperations.get_alternate_keys",false]],"get_relationship() (powerplatform.dataverse.operations.batch.batchtableoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchTableOperations.get_relationship",false]],"get_relationship() (powerplatform.dataverse.operations.tables.tableoperations method)":[[30,"PowerPlatform.Dataverse.operations.tables.TableOperations.get_relationship",false]],"gt() (in module powerplatform.dataverse.models.filters)":[[15,"PowerPlatform.Dataverse.models.filters.gt",false]],"has_errors (powerplatform.dataverse.models.batch.batchresult property)":[[13,"PowerPlatform.Dataverse.models.batch.BatchResult.has_errors",false]],"http_backoff (powerplatform.dataverse.core.config.dataverseconfig attribute)":[[4,"PowerPlatform.Dataverse.core.config.DataverseConfig.http_backoff",false]],"http_retries (powerplatform.dataverse.core.config.dataverseconfig attribute)":[[4,"PowerPlatform.Dataverse.core.config.DataverseConfig.http_retries",false]],"http_timeout (powerplatform.dataverse.core.config.dataverseconfig attribute)":[[4,"PowerPlatform.Dataverse.core.config.DataverseConfig.http_timeout",false]],"httperror":[[5,"PowerPlatform.Dataverse.core.errors.HttpError",false]],"id (powerplatform.dataverse.models.record.record attribute)":[[20,"PowerPlatform.Dataverse.models.record.Record.id",false]],"in_() (powerplatform.dataverse.models.filters.columnproxy method)":[[15,"PowerPlatform.Dataverse.models.filters.ColumnProxy.in_",false]],"include_annotations (powerplatform.dataverse.models.query_builder.queryparams attribute)":[[19,"PowerPlatform.Dataverse.models.query_builder.QueryParams.include_annotations",false]],"intersect_entity_name (powerplatform.dataverse.models.relationship.manytomanyrelationshipmetadata attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata.intersect_entity_name",false]],"is_not_null() (in module powerplatform.dataverse.models.filters)":[[15,"PowerPlatform.Dataverse.models.filters.is_not_null",false]],"is_not_null() (powerplatform.dataverse.models.filters.columnproxy method)":[[15,"PowerPlatform.Dataverse.models.filters.ColumnProxy.is_not_null",false]],"is_null() (in module powerplatform.dataverse.models.filters)":[[15,"PowerPlatform.Dataverse.models.filters.is_null",false]],"is_null() (powerplatform.dataverse.models.filters.columnproxy method)":[[15,"PowerPlatform.Dataverse.models.filters.ColumnProxy.is_null",false]],"is_primary (powerplatform.dataverse.models.table_info.columninfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.ColumnInfo.is_primary",false]],"is_required (powerplatform.dataverse.models.table_info.columninfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.ColumnInfo.is_required",false]],"is_success (powerplatform.dataverse.models.batch.batchitemresponse property)":[[13,"PowerPlatform.Dataverse.models.batch.BatchItemResponse.is_success",false]],"is_transient (powerplatform.dataverse.core.errors.dataverseerror attribute)":[[5,"PowerPlatform.Dataverse.core.errors.DataverseError.is_transient",false]],"items() (powerplatform.dataverse.models.record.record method)":[[20,"PowerPlatform.Dataverse.models.record.Record.items",false]],"items() (powerplatform.dataverse.models.table_info.tableinfo method)":[[22,"PowerPlatform.Dataverse.models.table_info.TableInfo.items",false]],"key_attributes (powerplatform.dataverse.models.table_info.alternatekeyinfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo.key_attributes",false]],"keys() (powerplatform.dataverse.models.record.record method)":[[20,"PowerPlatform.Dataverse.models.record.Record.keys",false]],"keys() (powerplatform.dataverse.models.table_info.tableinfo method)":[[22,"PowerPlatform.Dataverse.models.table_info.TableInfo.keys",false]],"label (class in powerplatform.dataverse.models.labels)":[[17,"PowerPlatform.Dataverse.models.labels.Label",false]],"label (powerplatform.dataverse.models.labels.localizedlabel attribute)":[[17,"PowerPlatform.Dataverse.models.labels.LocalizedLabel.label",false]],"language_code (powerplatform.dataverse.core.config.dataverseconfig attribute)":[[4,"PowerPlatform.Dataverse.core.config.DataverseConfig.language_code",false]],"language_code (powerplatform.dataverse.models.labels.localizedlabel attribute)":[[17,"PowerPlatform.Dataverse.models.labels.LocalizedLabel.language_code",false]],"le() (in module powerplatform.dataverse.models.filters)":[[15,"PowerPlatform.Dataverse.models.filters.le",false]],"like() (powerplatform.dataverse.models.filters.columnproxy method)":[[15,"PowerPlatform.Dataverse.models.filters.ColumnProxy.like",false]],"list() (powerplatform.dataverse.operations.batch.batchrecordoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchRecordOperations.list",false]],"list() (powerplatform.dataverse.operations.batch.batchtableoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchTableOperations.list",false]],"list() (powerplatform.dataverse.operations.records.recordoperations method)":[[29,"PowerPlatform.Dataverse.operations.records.RecordOperations.list",false]],"list() (powerplatform.dataverse.operations.tables.tableoperations method)":[[30,"PowerPlatform.Dataverse.operations.tables.TableOperations.list",false]],"list_columns() (powerplatform.dataverse.operations.tables.tableoperations method)":[[30,"PowerPlatform.Dataverse.operations.tables.TableOperations.list_columns",false]],"list_pages() (powerplatform.dataverse.operations.records.recordoperations method)":[[29,"PowerPlatform.Dataverse.operations.records.RecordOperations.list_pages",false]],"list_relationships() (powerplatform.dataverse.operations.tables.tableoperations method)":[[30,"PowerPlatform.Dataverse.operations.tables.TableOperations.list_relationships",false]],"list_table_relationships() (powerplatform.dataverse.operations.tables.tableoperations method)":[[30,"PowerPlatform.Dataverse.operations.tables.TableOperations.list_table_relationships",false]],"localized_labels (powerplatform.dataverse.models.labels.label attribute)":[[17,"PowerPlatform.Dataverse.models.labels.Label.localized_labels",false]],"localizedlabel (class in powerplatform.dataverse.models.labels)":[[17,"PowerPlatform.Dataverse.models.labels.LocalizedLabel",false]],"log_config (powerplatform.dataverse.core.config.dataverseconfig attribute)":[[4,"PowerPlatform.Dataverse.core.config.DataverseConfig.log_config",false]],"log_file_prefix (powerplatform.dataverse.core.log_config.logconfig attribute)":[[7,"PowerPlatform.Dataverse.core.log_config.LogConfig.log_file_prefix",false]],"log_folder (powerplatform.dataverse.core.log_config.logconfig attribute)":[[7,"PowerPlatform.Dataverse.core.log_config.LogConfig.log_folder",false]],"log_level (powerplatform.dataverse.core.log_config.logconfig attribute)":[[7,"PowerPlatform.Dataverse.core.log_config.LogConfig.log_level",false]],"logconfig (class in powerplatform.dataverse.core.log_config)":[[7,"PowerPlatform.Dataverse.core.log_config.LogConfig",false]],"logical_name (powerplatform.dataverse.models.table_info.columninfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.ColumnInfo.logical_name",false]],"logical_name (powerplatform.dataverse.models.table_info.tableinfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.TableInfo.logical_name",false]],"lookup_schema_name (powerplatform.dataverse.models.relationship.relationshipinfo attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.RelationshipInfo.lookup_schema_name",false]],"lookupattributemetadata (class in powerplatform.dataverse.models.relationship)":[[21,"PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata",false]],"lt() (in module powerplatform.dataverse.models.filters)":[[15,"PowerPlatform.Dataverse.models.filters.lt",false]],"main() (in module powerplatform.dataverse.migration.migrate_v0_to_v1)":[[12,"PowerPlatform.Dataverse.migration.migrate_v0_to_v1.main",false]],"manytomanyrelationshipmetadata (class in powerplatform.dataverse.models.relationship)":[[21,"PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata",false]],"max_body_bytes (powerplatform.dataverse.core.log_config.logconfig attribute)":[[7,"PowerPlatform.Dataverse.core.log_config.LogConfig.max_body_bytes",false]],"max_file_bytes (powerplatform.dataverse.core.log_config.logconfig attribute)":[[7,"PowerPlatform.Dataverse.core.log_config.LogConfig.max_file_bytes",false]],"max_length (powerplatform.dataverse.models.table_info.columninfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.ColumnInfo.max_length",false]],"merge (powerplatform.dataverse.models.relationship.cascadeconfiguration attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.CascadeConfiguration.merge",false]],"message (powerplatform.dataverse.core.errors.dataverseerror attribute)":[[5,"PowerPlatform.Dataverse.core.errors.DataverseError.message",false]],"metadata_id (powerplatform.dataverse.models.table_info.alternatekeyinfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo.metadata_id",false]],"metadata_id (powerplatform.dataverse.models.table_info.tableinfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.TableInfo.metadata_id",false]],"metadataerror":[[5,"PowerPlatform.Dataverse.core.errors.MetadataError",false]],"migrate_file() (in module powerplatform.dataverse.migration.migrate_v0_to_v1)":[[12,"PowerPlatform.Dataverse.migration.migrate_v0_to_v1.migrate_file",false]],"migrate_source() (in module powerplatform.dataverse.migration.migrate_v0_to_v1)":[[12,"PowerPlatform.Dataverse.migration.migrate_v0_to_v1.migrate_source",false]],"module":[[0,"module-PowerPlatform.Dataverse.claude_skill",false],[1,"module-PowerPlatform.Dataverse.client",false],[2,"module-PowerPlatform.Dataverse.common.constants",false],[3,"module-PowerPlatform.Dataverse.common",false],[4,"module-PowerPlatform.Dataverse.core.config",false],[5,"module-PowerPlatform.Dataverse.core.errors",false],[6,"module-PowerPlatform.Dataverse.core",false],[7,"module-PowerPlatform.Dataverse.core.log_config",false],[8,"module-PowerPlatform.Dataverse.data",false],[9,"module-PowerPlatform.Dataverse.extensions",false],[10,"module-PowerPlatform.Dataverse",false],[11,"module-PowerPlatform.Dataverse.migration",false],[12,"module-PowerPlatform.Dataverse.migration.migrate_v0_to_v1",false],[13,"module-PowerPlatform.Dataverse.models.batch",false],[14,"module-PowerPlatform.Dataverse.models.fetchxml_query",false],[15,"module-PowerPlatform.Dataverse.models.filters",false],[16,"module-PowerPlatform.Dataverse.models",false],[17,"module-PowerPlatform.Dataverse.models.labels",false],[18,"module-PowerPlatform.Dataverse.models.protocol",false],[19,"module-PowerPlatform.Dataverse.models.query_builder",false],[20,"module-PowerPlatform.Dataverse.models.record",false],[21,"module-PowerPlatform.Dataverse.models.relationship",false],[22,"module-PowerPlatform.Dataverse.models.table_info",false],[23,"module-PowerPlatform.Dataverse.models.upsert",false],[24,"module-PowerPlatform.Dataverse.operations.batch",false],[25,"module-PowerPlatform.Dataverse.operations.dataframe",false],[26,"module-PowerPlatform.Dataverse.operations.files",false],[27,"module-PowerPlatform.Dataverse.operations",false],[28,"module-PowerPlatform.Dataverse.operations.query",false],[29,"module-PowerPlatform.Dataverse.operations.records",false],[30,"module-PowerPlatform.Dataverse.operations.tables",false],[31,"module-PowerPlatform.Dataverse.utils",false],[32,"module-PowerPlatform",false]],"ne() (in module powerplatform.dataverse.models.filters)":[[15,"PowerPlatform.Dataverse.models.filters.ne",false]],"new() (powerplatform.dataverse.operations.batch.batchoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchOperations.new",false]],"not_between() (in module powerplatform.dataverse.models.filters)":[[15,"PowerPlatform.Dataverse.models.filters.not_between",false]],"not_between() (powerplatform.dataverse.models.filters.columnproxy method)":[[15,"PowerPlatform.Dataverse.models.filters.ColumnProxy.not_between",false]],"not_in() (in module powerplatform.dataverse.models.filters)":[[15,"PowerPlatform.Dataverse.models.filters.not_in",false]],"not_in() (powerplatform.dataverse.models.filters.columnproxy method)":[[15,"PowerPlatform.Dataverse.models.filters.ColumnProxy.not_in",false]],"not_like() (powerplatform.dataverse.models.filters.columnproxy method)":[[15,"PowerPlatform.Dataverse.models.filters.ColumnProxy.not_like",false]],"odata_bind() (powerplatform.dataverse.operations.query.queryoperations method)":[[28,"PowerPlatform.Dataverse.operations.query.QueryOperations.odata_bind",false]],"odata_expand() (powerplatform.dataverse.operations.query.queryoperations method)":[[28,"PowerPlatform.Dataverse.operations.query.QueryOperations.odata_expand",false]],"odata_expands() (powerplatform.dataverse.operations.query.queryoperations method)":[[28,"PowerPlatform.Dataverse.operations.query.QueryOperations.odata_expands",false]],"odata_select() (powerplatform.dataverse.operations.query.queryoperations method)":[[28,"PowerPlatform.Dataverse.operations.query.QueryOperations.odata_select",false]],"odata_type_label (in module powerplatform.dataverse.common.constants)":[[2,"PowerPlatform.Dataverse.common.constants.ODATA_TYPE_LABEL",false]],"odata_type_localized_label (in module powerplatform.dataverse.common.constants)":[[2,"PowerPlatform.Dataverse.common.constants.ODATA_TYPE_LOCALIZED_LABEL",false]],"odata_type_lookup_attribute (in module powerplatform.dataverse.common.constants)":[[2,"PowerPlatform.Dataverse.common.constants.ODATA_TYPE_LOOKUP_ATTRIBUTE",false]],"odata_type_many_to_many_relationship (in module powerplatform.dataverse.common.constants)":[[2,"PowerPlatform.Dataverse.common.constants.ODATA_TYPE_MANY_TO_MANY_RELATIONSHIP",false]],"odata_type_one_to_many_relationship (in module powerplatform.dataverse.common.constants)":[[2,"PowerPlatform.Dataverse.common.constants.ODATA_TYPE_ONE_TO_MANY_RELATIONSHIP",false]],"onetomanyrelationshipmetadata (class in powerplatform.dataverse.models.relationship)":[[21,"PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata",false]],"operation_context (powerplatform.dataverse.core.config.dataverseconfig attribute)":[[4,"PowerPlatform.Dataverse.core.config.DataverseConfig.operation_context",false]],"operationcontext (class in powerplatform.dataverse.core.config)":[[4,"PowerPlatform.Dataverse.core.config.OperationContext",false]],"order_by() (powerplatform.dataverse.models.query_builder.expandoption method)":[[19,"PowerPlatform.Dataverse.models.query_builder.ExpandOption.order_by",false]],"orderby (powerplatform.dataverse.models.query_builder.queryparams attribute)":[[19,"PowerPlatform.Dataverse.models.query_builder.QueryParams.orderby",false]],"page_size (powerplatform.dataverse.models.query_builder.queryparams attribute)":[[19,"PowerPlatform.Dataverse.models.query_builder.QueryParams.page_size",false]],"powerplatform":[[32,"module-PowerPlatform",false]],"powerplatform.dataverse":[[10,"module-PowerPlatform.Dataverse",false]],"powerplatform.dataverse.claude_skill":[[0,"module-PowerPlatform.Dataverse.claude_skill",false]],"powerplatform.dataverse.client":[[1,"module-PowerPlatform.Dataverse.client",false]],"powerplatform.dataverse.common":[[3,"module-PowerPlatform.Dataverse.common",false]],"powerplatform.dataverse.common.constants":[[2,"module-PowerPlatform.Dataverse.common.constants",false]],"powerplatform.dataverse.core":[[6,"module-PowerPlatform.Dataverse.core",false]],"powerplatform.dataverse.core.config":[[4,"module-PowerPlatform.Dataverse.core.config",false]],"powerplatform.dataverse.core.errors":[[5,"module-PowerPlatform.Dataverse.core.errors",false]],"powerplatform.dataverse.core.log_config":[[7,"module-PowerPlatform.Dataverse.core.log_config",false]],"powerplatform.dataverse.data":[[8,"module-PowerPlatform.Dataverse.data",false]],"powerplatform.dataverse.extensions":[[9,"module-PowerPlatform.Dataverse.extensions",false]],"powerplatform.dataverse.migration":[[11,"module-PowerPlatform.Dataverse.migration",false]],"powerplatform.dataverse.migration.migrate_v0_to_v1":[[12,"module-PowerPlatform.Dataverse.migration.migrate_v0_to_v1",false]],"powerplatform.dataverse.models":[[16,"module-PowerPlatform.Dataverse.models",false]],"powerplatform.dataverse.models.batch":[[13,"module-PowerPlatform.Dataverse.models.batch",false]],"powerplatform.dataverse.models.fetchxml_query":[[14,"module-PowerPlatform.Dataverse.models.fetchxml_query",false]],"powerplatform.dataverse.models.filters":[[15,"module-PowerPlatform.Dataverse.models.filters",false]],"powerplatform.dataverse.models.labels":[[17,"module-PowerPlatform.Dataverse.models.labels",false]],"powerplatform.dataverse.models.protocol":[[18,"module-PowerPlatform.Dataverse.models.protocol",false]],"powerplatform.dataverse.models.query_builder":[[19,"module-PowerPlatform.Dataverse.models.query_builder",false]],"powerplatform.dataverse.models.record":[[20,"module-PowerPlatform.Dataverse.models.record",false]],"powerplatform.dataverse.models.relationship":[[21,"module-PowerPlatform.Dataverse.models.relationship",false]],"powerplatform.dataverse.models.table_info":[[22,"module-PowerPlatform.Dataverse.models.table_info",false]],"powerplatform.dataverse.models.upsert":[[23,"module-PowerPlatform.Dataverse.models.upsert",false]],"powerplatform.dataverse.operations":[[27,"module-PowerPlatform.Dataverse.operations",false]],"powerplatform.dataverse.operations.batch":[[24,"module-PowerPlatform.Dataverse.operations.batch",false]],"powerplatform.dataverse.operations.dataframe":[[25,"module-PowerPlatform.Dataverse.operations.dataframe",false]],"powerplatform.dataverse.operations.files":[[26,"module-PowerPlatform.Dataverse.operations.files",false]],"powerplatform.dataverse.operations.query":[[28,"module-PowerPlatform.Dataverse.operations.query",false]],"powerplatform.dataverse.operations.records":[[29,"module-PowerPlatform.Dataverse.operations.records",false]],"powerplatform.dataverse.operations.tables":[[30,"module-PowerPlatform.Dataverse.operations.tables",false]],"powerplatform.dataverse.utils":[[31,"module-PowerPlatform.Dataverse.utils",false]],"primary_id_attribute (powerplatform.dataverse.models.table_info.tableinfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.TableInfo.primary_id_attribute",false]],"primary_name_attribute (powerplatform.dataverse.models.table_info.tableinfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.TableInfo.primary_name_attribute",false]],"query (powerplatform.dataverse.client.dataverseclient attribute)":[[1,"PowerPlatform.Dataverse.client.DataverseClient.query",false]],"query (powerplatform.dataverse.operations.batch.batchrequest attribute)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchRequest.query",false]],"querybuilder (class in powerplatform.dataverse.models.query_builder)":[[19,"PowerPlatform.Dataverse.models.query_builder.QueryBuilder",false]],"queryoperations (class in powerplatform.dataverse.operations.query)":[[28,"PowerPlatform.Dataverse.operations.query.QueryOperations",false]],"queryparams (class in powerplatform.dataverse.models.query_builder)":[[19,"PowerPlatform.Dataverse.models.query_builder.QueryParams",false]],"queryresult (class in powerplatform.dataverse.models.record)":[[20,"PowerPlatform.Dataverse.models.record.QueryResult",false]],"raw() (in module powerplatform.dataverse.models.filters)":[[15,"PowerPlatform.Dataverse.models.filters.raw",false]],"record (class in powerplatform.dataverse.models.record)":[[20,"PowerPlatform.Dataverse.models.record.Record",false]],"record (powerplatform.dataverse.models.upsert.upsertitem attribute)":[[23,"PowerPlatform.Dataverse.models.upsert.UpsertItem.record",false]],"recordoperations (class in powerplatform.dataverse.operations.records)":[[29,"PowerPlatform.Dataverse.operations.records.RecordOperations",false]],"records (powerplatform.dataverse.client.dataverseclient attribute)":[[1,"PowerPlatform.Dataverse.client.DataverseClient.records",false]],"records (powerplatform.dataverse.models.record.queryresult attribute)":[[20,"PowerPlatform.Dataverse.models.record.QueryResult.records",false]],"records (powerplatform.dataverse.operations.batch.batchrequest attribute)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchRequest.records",false]],"records (powerplatform.dataverse.operations.batch.changeset attribute)":[[24,"PowerPlatform.Dataverse.operations.batch.ChangeSet.records",false]],"redacted_headers (powerplatform.dataverse.core.log_config.logconfig attribute)":[[7,"PowerPlatform.Dataverse.core.log_config.LogConfig.redacted_headers",false]],"referenced_attribute (powerplatform.dataverse.models.relationship.onetomanyrelationshipmetadata attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata.referenced_attribute",false]],"referenced_entity (powerplatform.dataverse.models.relationship.onetomanyrelationshipmetadata attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata.referenced_entity",false]],"referenced_entity (powerplatform.dataverse.models.relationship.relationshipinfo attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.RelationshipInfo.referenced_entity",false]],"referencing_attribute (powerplatform.dataverse.models.relationship.onetomanyrelationshipmetadata attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata.referencing_attribute",false]],"referencing_entity (powerplatform.dataverse.models.relationship.onetomanyrelationshipmetadata attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata.referencing_entity",false]],"referencing_entity (powerplatform.dataverse.models.relationship.relationshipinfo attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.RelationshipInfo.referencing_entity",false]],"relation (powerplatform.dataverse.models.query_builder.expandoption attribute)":[[19,"PowerPlatform.Dataverse.models.query_builder.ExpandOption.relation",false]],"relationship_id (powerplatform.dataverse.models.relationship.relationshipinfo attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.RelationshipInfo.relationship_id",false]],"relationship_schema_name (powerplatform.dataverse.models.relationship.relationshipinfo attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.RelationshipInfo.relationship_schema_name",false]],"relationship_type (powerplatform.dataverse.models.relationship.relationshipinfo attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.RelationshipInfo.relationship_type",false]],"relationshipinfo (class in powerplatform.dataverse.models.relationship)":[[21,"PowerPlatform.Dataverse.models.relationship.RelationshipInfo",false]],"remove_columns() (powerplatform.dataverse.operations.batch.batchtableoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchTableOperations.remove_columns",false]],"remove_columns() (powerplatform.dataverse.operations.tables.tableoperations method)":[[30,"PowerPlatform.Dataverse.operations.tables.TableOperations.remove_columns",false]],"reparent (powerplatform.dataverse.models.relationship.cascadeconfiguration attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.CascadeConfiguration.reparent",false]],"required_level (powerplatform.dataverse.models.relationship.lookupattributemetadata attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata.required_level",false]],"responses (powerplatform.dataverse.models.batch.batchresult attribute)":[[13,"PowerPlatform.Dataverse.models.batch.BatchResult.responses",false]],"retrieve() (powerplatform.dataverse.operations.batch.batchrecordoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchRecordOperations.retrieve",false]],"retrieve() (powerplatform.dataverse.operations.records.recordoperations method)":[[29,"PowerPlatform.Dataverse.operations.records.RecordOperations.retrieve",false]],"schema_name (powerplatform.dataverse.models.relationship.lookupattributemetadata attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata.schema_name",false]],"schema_name (powerplatform.dataverse.models.relationship.manytomanyrelationshipmetadata attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata.schema_name",false]],"schema_name (powerplatform.dataverse.models.relationship.onetomanyrelationshipmetadata attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata.schema_name",false]],"schema_name (powerplatform.dataverse.models.table_info.alternatekeyinfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo.schema_name",false]],"schema_name (powerplatform.dataverse.models.table_info.columninfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.ColumnInfo.schema_name",false]],"schema_name (powerplatform.dataverse.models.table_info.tableinfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.TableInfo.schema_name",false]],"select (powerplatform.dataverse.models.query_builder.queryparams attribute)":[[19,"PowerPlatform.Dataverse.models.query_builder.QueryParams.select",false]],"select() (powerplatform.dataverse.models.query_builder.expandoption method)":[[19,"PowerPlatform.Dataverse.models.query_builder.ExpandOption.select",false]],"share (powerplatform.dataverse.models.relationship.cascadeconfiguration attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.CascadeConfiguration.share",false]],"source (powerplatform.dataverse.core.errors.dataverseerror attribute)":[[5,"PowerPlatform.Dataverse.core.errors.DataverseError.source",false]],"sql() (powerplatform.dataverse.operations.batch.batchqueryoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchQueryOperations.sql",false]],"sql() (powerplatform.dataverse.operations.dataframe.dataframeoperations method)":[[25,"PowerPlatform.Dataverse.operations.dataframe.DataFrameOperations.sql",false]],"sql() (powerplatform.dataverse.operations.query.queryoperations method)":[[28,"PowerPlatform.Dataverse.operations.query.QueryOperations.sql",false]],"sql_columns() (powerplatform.dataverse.operations.query.queryoperations method)":[[28,"PowerPlatform.Dataverse.operations.query.QueryOperations.sql_columns",false]],"sqlparseerror":[[5,"PowerPlatform.Dataverse.core.errors.SQLParseError",false]],"startswith() (in module powerplatform.dataverse.models.filters)":[[15,"PowerPlatform.Dataverse.models.filters.startswith",false]],"startswith() (powerplatform.dataverse.models.filters.columnproxy method)":[[15,"PowerPlatform.Dataverse.models.filters.ColumnProxy.startswith",false]],"status (powerplatform.dataverse.models.table_info.alternatekeyinfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo.status",false]],"status_code (powerplatform.dataverse.core.errors.dataverseerror attribute)":[[5,"PowerPlatform.Dataverse.core.errors.DataverseError.status_code",false]],"status_code (powerplatform.dataverse.models.batch.batchitemresponse attribute)":[[13,"PowerPlatform.Dataverse.models.batch.BatchItemResponse.status_code",false]],"subcode (powerplatform.dataverse.core.errors.dataverseerror attribute)":[[5,"PowerPlatform.Dataverse.core.errors.DataverseError.subcode",false]],"succeeded (powerplatform.dataverse.models.batch.batchresult property)":[[13,"PowerPlatform.Dataverse.models.batch.BatchResult.succeeded",false]],"table (powerplatform.dataverse.models.query_builder.queryparams attribute)":[[19,"PowerPlatform.Dataverse.models.query_builder.QueryParams.table",false]],"table (powerplatform.dataverse.models.record.record attribute)":[[20,"PowerPlatform.Dataverse.models.record.Record.table",false]],"tableinfo (class in powerplatform.dataverse.models.table_info)":[[22,"PowerPlatform.Dataverse.models.table_info.TableInfo",false]],"tableoperations (class in powerplatform.dataverse.operations.tables)":[[30,"PowerPlatform.Dataverse.operations.tables.TableOperations",false]],"tables (powerplatform.dataverse.client.dataverseclient attribute)":[[1,"PowerPlatform.Dataverse.client.DataverseClient.tables",false]],"tables (powerplatform.dataverse.operations.batch.batchrequest attribute)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchRequest.tables",false]],"timestamp (powerplatform.dataverse.core.errors.dataverseerror attribute)":[[5,"PowerPlatform.Dataverse.core.errors.DataverseError.timestamp",false]],"to_dataframe() (powerplatform.dataverse.models.query_builder.querybuilder method)":[[19,"PowerPlatform.Dataverse.models.query_builder.QueryBuilder.to_dataframe",false]],"to_dataframe() (powerplatform.dataverse.models.record.queryresult method)":[[20,"PowerPlatform.Dataverse.models.record.QueryResult.to_dataframe",false]],"to_dict() (powerplatform.dataverse.core.errors.dataverseerror method)":[[5,"PowerPlatform.Dataverse.core.errors.DataverseError.to_dict",false]],"to_dict() (powerplatform.dataverse.models.labels.label method)":[[17,"PowerPlatform.Dataverse.models.labels.Label.to_dict",false]],"to_dict() (powerplatform.dataverse.models.labels.localizedlabel method)":[[17,"PowerPlatform.Dataverse.models.labels.LocalizedLabel.to_dict",false]],"to_dict() (powerplatform.dataverse.models.protocol.dataversemodel method)":[[18,"PowerPlatform.Dataverse.models.protocol.DataverseModel.to_dict",false]],"to_dict() (powerplatform.dataverse.models.record.record method)":[[20,"PowerPlatform.Dataverse.models.record.Record.to_dict",false]],"to_dict() (powerplatform.dataverse.models.relationship.cascadeconfiguration method)":[[21,"PowerPlatform.Dataverse.models.relationship.CascadeConfiguration.to_dict",false]],"to_dict() (powerplatform.dataverse.models.relationship.lookupattributemetadata method)":[[21,"PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata.to_dict",false]],"to_dict() (powerplatform.dataverse.models.relationship.manytomanyrelationshipmetadata method)":[[21,"PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata.to_dict",false]],"to_dict() (powerplatform.dataverse.models.relationship.onetomanyrelationshipmetadata method)":[[21,"PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata.to_dict",false]],"to_dict() (powerplatform.dataverse.models.table_info.tableinfo method)":[[22,"PowerPlatform.Dataverse.models.table_info.TableInfo.to_dict",false]],"to_odata() (powerplatform.dataverse.models.filters.filterexpression method)":[[15,"PowerPlatform.Dataverse.models.filters.FilterExpression.to_odata",false]],"to_odata() (powerplatform.dataverse.models.query_builder.expandoption method)":[[19,"PowerPlatform.Dataverse.models.query_builder.ExpandOption.to_odata",false]],"top (powerplatform.dataverse.models.query_builder.queryparams attribute)":[[19,"PowerPlatform.Dataverse.models.query_builder.QueryParams.top",false]],"top() (powerplatform.dataverse.models.query_builder.expandoption method)":[[19,"PowerPlatform.Dataverse.models.query_builder.ExpandOption.top",false]],"type (powerplatform.dataverse.models.table_info.columninfo attribute)":[[22,"PowerPlatform.Dataverse.models.table_info.ColumnInfo.type",false]],"unshare (powerplatform.dataverse.models.relationship.cascadeconfiguration attribute)":[[21,"PowerPlatform.Dataverse.models.relationship.CascadeConfiguration.unshare",false]],"update() (powerplatform.dataverse.operations.batch.batchdataframeoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchDataFrameOperations.update",false]],"update() (powerplatform.dataverse.operations.batch.batchrecordoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchRecordOperations.update",false]],"update() (powerplatform.dataverse.operations.batch.changesetrecordoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.ChangeSetRecordOperations.update",false]],"update() (powerplatform.dataverse.operations.dataframe.dataframeoperations method)":[[25,"PowerPlatform.Dataverse.operations.dataframe.DataFrameOperations.update",false]],"update() (powerplatform.dataverse.operations.records.recordoperations method)":[[29,"PowerPlatform.Dataverse.operations.records.RecordOperations.update",false]],"upload() (powerplatform.dataverse.operations.files.fileoperations method)":[[26,"PowerPlatform.Dataverse.operations.files.FileOperations.upload",false]],"upsert() (powerplatform.dataverse.operations.batch.batchrecordoperations method)":[[24,"PowerPlatform.Dataverse.operations.batch.BatchRecordOperations.upsert",false]],"upsert() (powerplatform.dataverse.operations.records.recordoperations method)":[[29,"PowerPlatform.Dataverse.operations.records.RecordOperations.upsert",false]],"upsertitem (class in powerplatform.dataverse.models.upsert)":[[23,"PowerPlatform.Dataverse.models.upsert.UpsertItem",false]],"user_agent_context (powerplatform.dataverse.core.config.operationcontext attribute)":[[4,"PowerPlatform.Dataverse.core.config.OperationContext.user_agent_context",false]],"user_localized_label (powerplatform.dataverse.models.labels.label attribute)":[[17,"PowerPlatform.Dataverse.models.labels.Label.user_localized_label",false]],"validationerror":[[5,"PowerPlatform.Dataverse.core.errors.ValidationError",false]],"values() (powerplatform.dataverse.models.record.record method)":[[20,"PowerPlatform.Dataverse.models.record.Record.values",false]],"values() (powerplatform.dataverse.models.table_info.tableinfo method)":[[22,"PowerPlatform.Dataverse.models.table_info.TableInfo.values",false]]},"objects":{"":[[32,0,0,"-","PowerPlatform"]],"PowerPlatform":[[10,0,0,"-","Dataverse"]],"PowerPlatform.Dataverse":[[0,0,0,"-","claude_skill"],[1,0,0,"-","client"],[3,0,0,"-","common"],[6,0,0,"-","core"],[8,0,0,"-","data"],[9,0,0,"-","extensions"],[11,0,0,"-","migration"],[16,0,0,"-","models"],[27,0,0,"-","operations"],[31,0,0,"-","utils"]],"PowerPlatform.Dataverse.client":[[1,1,1,"","DataverseClient"]],"PowerPlatform.Dataverse.client.DataverseClient":[[1,2,1,"","auth"],[1,2,1,"","batch"],[1,3,1,"","close"],[1,2,1,"","dataframe"],[1,2,1,"","files"],[1,3,1,"","flush_cache"],[1,2,1,"","query"],[1,2,1,"","records"],[1,2,1,"","tables"]],"PowerPlatform.Dataverse.common":[[2,0,0,"-","constants"]],"PowerPlatform.Dataverse.common.constants":[[2,4,1,"","CASCADE_BEHAVIOR_CASCADE"],[2,4,1,"","CASCADE_BEHAVIOR_NO_CASCADE"],[2,4,1,"","CASCADE_BEHAVIOR_REMOVE_LINK"],[2,4,1,"","CASCADE_BEHAVIOR_RESTRICT"],[2,4,1,"","ODATA_TYPE_LABEL"],[2,4,1,"","ODATA_TYPE_LOCALIZED_LABEL"],[2,4,1,"","ODATA_TYPE_LOOKUP_ATTRIBUTE"],[2,4,1,"","ODATA_TYPE_MANY_TO_MANY_RELATIONSHIP"],[2,4,1,"","ODATA_TYPE_ONE_TO_MANY_RELATIONSHIP"]],"PowerPlatform.Dataverse.core":[[4,0,0,"-","config"],[5,0,0,"-","errors"],[7,0,0,"-","log_config"]],"PowerPlatform.Dataverse.core.config":[[4,1,1,"","DataverseConfig"],[4,1,1,"","OperationContext"]],"PowerPlatform.Dataverse.core.config.DataverseConfig":[[4,3,1,"","from_env"],[4,2,1,"","http_backoff"],[4,2,1,"","http_retries"],[4,2,1,"","http_timeout"],[4,2,1,"","language_code"],[4,2,1,"","log_config"],[4,2,1,"","operation_context"]],"PowerPlatform.Dataverse.core.config.OperationContext":[[4,2,1,"","user_agent_context"]],"PowerPlatform.Dataverse.core.errors":[[5,5,1,"","DataverseError"],[5,5,1,"","HttpError"],[5,5,1,"","MetadataError"],[5,5,1,"","SQLParseError"],[5,5,1,"","ValidationError"]],"PowerPlatform.Dataverse.core.errors.DataverseError":[[5,2,1,"","code"],[5,2,1,"","details"],[5,2,1,"","is_transient"],[5,2,1,"","message"],[5,2,1,"","source"],[5,2,1,"","status_code"],[5,2,1,"","subcode"],[5,2,1,"","timestamp"],[5,3,1,"","to_dict"]],"PowerPlatform.Dataverse.core.log_config":[[7,1,1,"","LogConfig"]],"PowerPlatform.Dataverse.core.log_config.LogConfig":[[7,2,1,"","backup_count"],[7,2,1,"","log_file_prefix"],[7,2,1,"","log_folder"],[7,2,1,"","log_level"],[7,2,1,"","max_body_bytes"],[7,2,1,"","max_file_bytes"],[7,2,1,"","redacted_headers"]],"PowerPlatform.Dataverse.migration":[[12,0,0,"-","migrate_v0_to_v1"]],"PowerPlatform.Dataverse.migration.migrate_v0_to_v1":[[12,6,1,"","find_manual_patterns"],[12,6,1,"","main"],[12,6,1,"","migrate_file"],[12,6,1,"","migrate_source"]],"PowerPlatform.Dataverse.models":[[13,0,0,"-","batch"],[14,0,0,"-","fetchxml_query"],[15,0,0,"-","filters"],[17,0,0,"-","labels"],[18,0,0,"-","protocol"],[19,0,0,"-","query_builder"],[20,0,0,"-","record"],[21,0,0,"-","relationship"],[22,0,0,"-","table_info"],[23,0,0,"-","upsert"]],"PowerPlatform.Dataverse.models.batch":[[13,1,1,"","BatchItemResponse"],[13,1,1,"","BatchResult"]],"PowerPlatform.Dataverse.models.batch.BatchItemResponse":[[13,2,1,"","content_id"],[13,2,1,"","data"],[13,2,1,"","entity_id"],[13,2,1,"","error_code"],[13,2,1,"","error_message"],[13,7,1,"","is_success"],[13,2,1,"","status_code"]],"PowerPlatform.Dataverse.models.batch.BatchResult":[[13,7,1,"","entity_ids"],[13,7,1,"","failed"],[13,7,1,"","has_errors"],[13,2,1,"","responses"],[13,7,1,"","succeeded"]],"PowerPlatform.Dataverse.models.fetchxml_query":[[14,1,1,"","FetchXmlQuery"]],"PowerPlatform.Dataverse.models.fetchxml_query.FetchXmlQuery":[[14,3,1,"","execute"],[14,3,1,"","execute_pages"]],"PowerPlatform.Dataverse.models.filters":[[15,1,1,"","ColumnProxy"],[15,1,1,"","FilterExpression"],[15,6,1,"","between"],[15,6,1,"","col"],[15,6,1,"","contains"],[15,6,1,"","endswith"],[15,6,1,"","eq"],[15,6,1,"","filter_in"],[15,6,1,"","ge"],[15,6,1,"","gt"],[15,6,1,"","is_not_null"],[15,6,1,"","is_null"],[15,6,1,"","le"],[15,6,1,"","lt"],[15,6,1,"","ne"],[15,6,1,"","not_between"],[15,6,1,"","not_in"],[15,6,1,"","raw"],[15,6,1,"","startswith"]],"PowerPlatform.Dataverse.models.filters.ColumnProxy":[[15,3,1,"","between"],[15,3,1,"","contains"],[15,3,1,"","endswith"],[15,3,1,"","in_"],[15,3,1,"","is_not_null"],[15,3,1,"","is_null"],[15,3,1,"","like"],[15,3,1,"","not_between"],[15,3,1,"","not_in"],[15,3,1,"","not_like"],[15,3,1,"","startswith"]],"PowerPlatform.Dataverse.models.filters.FilterExpression":[[15,3,1,"","to_odata"]],"PowerPlatform.Dataverse.models.labels":[[17,1,1,"","Label"],[17,1,1,"","LocalizedLabel"]],"PowerPlatform.Dataverse.models.labels.Label":[[17,2,1,"","additional_properties"],[17,2,1,"","localized_labels"],[17,3,1,"","to_dict"],[17,2,1,"","user_localized_label"]],"PowerPlatform.Dataverse.models.labels.LocalizedLabel":[[17,2,1,"","additional_properties"],[17,2,1,"","label"],[17,2,1,"","language_code"],[17,3,1,"","to_dict"]],"PowerPlatform.Dataverse.models.protocol":[[18,1,1,"","DataverseModel"]],"PowerPlatform.Dataverse.models.protocol.DataverseModel":[[18,3,1,"","from_dict"],[18,3,1,"","to_dict"]],"PowerPlatform.Dataverse.models.query_builder":[[19,1,1,"","ExpandOption"],[19,1,1,"","QueryBuilder"],[19,1,1,"","QueryParams"]],"PowerPlatform.Dataverse.models.query_builder.ExpandOption":[[19,3,1,"","filter"],[19,3,1,"","order_by"],[19,2,1,"","relation"],[19,3,1,"","select"],[19,3,1,"","to_odata"],[19,3,1,"","top"]],"PowerPlatform.Dataverse.models.query_builder.QueryBuilder":[[19,3,1,"","execute"],[19,3,1,"","execute_pages"],[19,3,1,"","to_dataframe"]],"PowerPlatform.Dataverse.models.query_builder.QueryParams":[[19,2,1,"","count"],[19,2,1,"","expand"],[19,2,1,"","filter"],[19,2,1,"","include_annotations"],[19,2,1,"","orderby"],[19,2,1,"","page_size"],[19,2,1,"","select"],[19,2,1,"","table"],[19,2,1,"","top"]],"PowerPlatform.Dataverse.models.record":[[20,1,1,"","QueryResult"],[20,1,1,"","Record"]],"PowerPlatform.Dataverse.models.record.QueryResult":[[20,3,1,"","first"],[20,2,1,"","records"],[20,3,1,"","to_dataframe"]],"PowerPlatform.Dataverse.models.record.Record":[[20,2,1,"","data"],[20,2,1,"","etag"],[20,3,1,"","from_api_response"],[20,3,1,"","get"],[20,2,1,"","id"],[20,3,1,"","items"],[20,3,1,"","keys"],[20,2,1,"","table"],[20,3,1,"","to_dict"],[20,3,1,"","values"]],"PowerPlatform.Dataverse.models.relationship":[[21,1,1,"","CascadeConfiguration"],[21,1,1,"","LookupAttributeMetadata"],[21,1,1,"","ManyToManyRelationshipMetadata"],[21,1,1,"","OneToManyRelationshipMetadata"],[21,1,1,"","RelationshipInfo"]],"PowerPlatform.Dataverse.models.relationship.CascadeConfiguration":[[21,2,1,"","additional_properties"],[21,2,1,"","assign"],[21,2,1,"","delete"],[21,2,1,"","merge"],[21,2,1,"","reparent"],[21,2,1,"","share"],[21,3,1,"","to_dict"],[21,2,1,"","unshare"]],"PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata":[[21,2,1,"","additional_properties"],[21,2,1,"","description"],[21,2,1,"","display_name"],[21,2,1,"","required_level"],[21,2,1,"","schema_name"],[21,3,1,"","to_dict"]],"PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata":[[21,2,1,"","additional_properties"],[21,2,1,"","entity1_logical_name"],[21,2,1,"","entity2_logical_name"],[21,2,1,"","intersect_entity_name"],[21,2,1,"","schema_name"],[21,3,1,"","to_dict"]],"PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata":[[21,2,1,"","additional_properties"],[21,2,1,"","cascade_configuration"],[21,2,1,"","referenced_attribute"],[21,2,1,"","referenced_entity"],[21,2,1,"","referencing_attribute"],[21,2,1,"","referencing_entity"],[21,2,1,"","schema_name"],[21,3,1,"","to_dict"]],"PowerPlatform.Dataverse.models.relationship.RelationshipInfo":[[21,2,1,"","entity1_logical_name"],[21,2,1,"","entity2_logical_name"],[21,3,1,"","from_api_response"],[21,3,1,"","from_many_to_many"],[21,3,1,"","from_one_to_many"],[21,2,1,"","lookup_schema_name"],[21,2,1,"","referenced_entity"],[21,2,1,"","referencing_entity"],[21,2,1,"","relationship_id"],[21,2,1,"","relationship_schema_name"],[21,2,1,"","relationship_type"]],"PowerPlatform.Dataverse.models.table_info":[[22,1,1,"","AlternateKeyInfo"],[22,1,1,"","ColumnInfo"],[22,1,1,"","TableInfo"]],"PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo":[[22,3,1,"","from_api_response"],[22,2,1,"","key_attributes"],[22,2,1,"","metadata_id"],[22,2,1,"","schema_name"],[22,2,1,"","status"]],"PowerPlatform.Dataverse.models.table_info.ColumnInfo":[[22,2,1,"","description"],[22,2,1,"","display_name"],[22,3,1,"","from_api_response"],[22,2,1,"","is_primary"],[22,2,1,"","is_required"],[22,2,1,"","logical_name"],[22,2,1,"","max_length"],[22,2,1,"","schema_name"],[22,2,1,"","type"]],"PowerPlatform.Dataverse.models.table_info.TableInfo":[[22,2,1,"","columns"],[22,2,1,"","columns_created"],[22,2,1,"","description"],[22,2,1,"","display_name"],[22,2,1,"","entity_set_name"],[22,3,1,"","from_api_response"],[22,3,1,"","from_dict"],[22,3,1,"","get"],[22,3,1,"","items"],[22,3,1,"","keys"],[22,2,1,"","logical_name"],[22,2,1,"","metadata_id"],[22,2,1,"","primary_id_attribute"],[22,2,1,"","primary_name_attribute"],[22,2,1,"","schema_name"],[22,3,1,"","to_dict"],[22,3,1,"","values"]],"PowerPlatform.Dataverse.models.upsert":[[23,1,1,"","UpsertItem"]],"PowerPlatform.Dataverse.models.upsert.UpsertItem":[[23,2,1,"","alternate_key"],[23,2,1,"","record"]],"PowerPlatform.Dataverse.operations":[[24,0,0,"-","batch"],[25,0,0,"-","dataframe"],[26,0,0,"-","files"],[28,0,0,"-","query"],[29,0,0,"-","records"],[30,0,0,"-","tables"]],"PowerPlatform.Dataverse.operations.batch":[[24,1,1,"","BatchDataFrameOperations"],[24,1,1,"","BatchOperations"],[24,1,1,"","BatchQueryOperations"],[24,1,1,"","BatchRecordOperations"],[24,1,1,"","BatchRequest"],[24,1,1,"","BatchTableOperations"],[24,1,1,"","ChangeSet"],[24,1,1,"","ChangeSetRecordOperations"]],"PowerPlatform.Dataverse.operations.batch.BatchDataFrameOperations":[[24,3,1,"","create"],[24,3,1,"","delete"],[24,3,1,"","update"]],"PowerPlatform.Dataverse.operations.batch.BatchOperations":[[24,3,1,"","new"]],"PowerPlatform.Dataverse.operations.batch.BatchQueryOperations":[[24,3,1,"","sql"]],"PowerPlatform.Dataverse.operations.batch.BatchRecordOperations":[[24,3,1,"","create"],[24,3,1,"","delete"],[24,3,1,"","get"],[24,3,1,"","list"],[24,3,1,"","retrieve"],[24,3,1,"","update"],[24,3,1,"","upsert"]],"PowerPlatform.Dataverse.operations.batch.BatchRequest":[[24,3,1,"","changeset"],[24,2,1,"","dataframe"],[24,3,1,"","execute"],[24,2,1,"","query"],[24,2,1,"","records"],[24,2,1,"","tables"]],"PowerPlatform.Dataverse.operations.batch.BatchTableOperations":[[24,3,1,"","add_columns"],[24,3,1,"","create"],[24,3,1,"","create_lookup_field"],[24,3,1,"","create_many_to_many_relationship"],[24,3,1,"","create_one_to_many_relationship"],[24,3,1,"","delete"],[24,3,1,"","delete_relationship"],[24,3,1,"","get"],[24,3,1,"","get_relationship"],[24,3,1,"","list"],[24,3,1,"","remove_columns"]],"PowerPlatform.Dataverse.operations.batch.ChangeSet":[[24,2,1,"","records"]],"PowerPlatform.Dataverse.operations.batch.ChangeSetRecordOperations":[[24,3,1,"","create"],[24,3,1,"","delete"],[24,3,1,"","update"]],"PowerPlatform.Dataverse.operations.dataframe":[[25,1,1,"","DataFrameOperations"]],"PowerPlatform.Dataverse.operations.dataframe.DataFrameOperations":[[25,3,1,"","create"],[25,3,1,"","delete"],[25,3,1,"","get"],[25,3,1,"","sql"],[25,3,1,"","update"]],"PowerPlatform.Dataverse.operations.files":[[26,1,1,"","FileOperations"]],"PowerPlatform.Dataverse.operations.files.FileOperations":[[26,3,1,"","upload"]],"PowerPlatform.Dataverse.operations.query":[[28,1,1,"","QueryOperations"]],"PowerPlatform.Dataverse.operations.query.QueryOperations":[[28,3,1,"","builder"],[28,3,1,"","fetchxml"],[28,3,1,"","odata_bind"],[28,3,1,"","odata_expand"],[28,3,1,"","odata_expands"],[28,3,1,"","odata_select"],[28,3,1,"","sql"],[28,3,1,"","sql_columns"]],"PowerPlatform.Dataverse.operations.records":[[29,1,1,"","RecordOperations"]],"PowerPlatform.Dataverse.operations.records.RecordOperations":[[29,3,1,"","create"],[29,3,1,"","delete"],[29,3,1,"","get"],[29,3,1,"","list"],[29,3,1,"","list_pages"],[29,3,1,"","retrieve"],[29,3,1,"","update"],[29,3,1,"","upsert"]],"PowerPlatform.Dataverse.operations.tables":[[30,1,1,"","TableOperations"]],"PowerPlatform.Dataverse.operations.tables.TableOperations":[[30,3,1,"","add_columns"],[30,3,1,"","create"],[30,3,1,"","create_alternate_key"],[30,3,1,"","create_lookup_field"],[30,3,1,"","create_many_to_many_relationship"],[30,3,1,"","create_one_to_many_relationship"],[30,3,1,"","delete"],[30,3,1,"","delete_alternate_key"],[30,3,1,"","delete_relationship"],[30,3,1,"","get"],[30,3,1,"","get_alternate_keys"],[30,3,1,"","get_relationship"],[30,3,1,"","list"],[30,3,1,"","list_columns"],[30,3,1,"","list_relationships"],[30,3,1,"","list_table_relationships"],[30,3,1,"","remove_columns"]]},"objnames":{"0":["py","module","Python module"],"1":["py","class","Python class"],"2":["py","attribute","Python attribute"],"3":["py","method","Python method"],"4":["py","data","Python data"],"5":["py","exception","Python exception"],"6":["py","function","Python function"],"7":["py","property","Python property"]},"objtypes":{"0":"py:module","1":"py:class","2":"py:attribute","3":"py:method","4":"py:data","5":"py:exception","6":"py:function","7":"py:property"},"terms":{"0b":12,"100_000":28,"10_485_760":7,"123456789abc":30,"1_000_000":[15,19,28],"20s":28,"2xx":13,"30s":28,"4xx":[5,24],"5xx":5,"A":[15,24,28,29],"AND":[15,28],"After":[1,5,30],"All":[1,13,14,19,24,25,29],"An":24,"At":19,"BETWEEN":28,"BY":[25,28],"Between":15,"Both":13,"By":30,"Cannot":1,"Do":[2,21,24],"Each":[19,24,25,28,29],"FROM":[24,25,28],"For":[13,25,28,30],"HAVING":28,"IN":28,"IS":28,"If":[1,4,15,19,20,21,24,25,26,28,29,30],"In":15,"It":1,"NOT":[12,28],"No":[14,28],"Not":[15,28],"ON":[25,28],"OR":[15,28],"On":24,"Or":12,"The":[1,4,14,17,19,21,24,25,26,28,29,30],"Their":13,"These":[2,17,21],"This":[0,1,3,5,6,8,15,19,22,24,27,29,30,33],"WHERE":[24,25,28],"When":[4,7,24,25,26,28,29,30],"With":[19,28],"You":14,"Your":1,"__entity_logical_name__":18,"__entity_set_name__":18,"_base":28,"_batch":24,"_batchcontext":24,"_by_page_unset":19,"_changeset":24,"_create_t":22,"_get_table_info":22,"_name":30,"_querybuilderbas":19,"abc":29,"abstractmethod":15,"acc":[23,24,29],"accept":24,"access":[8,13,20,22,24,25,26,28,29,30],"accident":19,"account":[1,17,18,19,20,21,22,24,25,26,28,29,30],"account_id":[20,24,25,26,29],"account_task":19,"accountid":[21,24,25,28],"accountnumb":[23,24,29],"acct":28,"acct_id":28,"accur":[5,19],"acm":24,"across":[3,14],"action":[2,13,24,25,29],"activ":[7,22,25,30],"ada":24,"adapt":31,"add":[1,12,13,18,24,25,29,30],"add_column":[12,13,24,30],"addit":[5,17,21,24],"additional_properti":[17,21],"address":4,"address1_postalcod":[23,29],"agent":[1,4],"aggreg":[25,28],"alia":28,"alic":24,"align":25,"allow":[1,4,19,24,30],"allowlist":4,"along":30,"alongsid":[25,29],"alreadi":[12,24,30],"also":30,"altern":[22,23,24,29,30],"alternate_key":[23,24,29],"alternatekeyinfo":[22,30],"alway":[26,28],"ani":[1,2,5,12,13,15,17,18,20,21,22,23,24,28,29,30],"annot":[20,24,25,29],"anoth":28,"api":[1,2,5,17,20,21,22,25,28,30,34],"app":4,"appear":13,"append":[1,4,7],"appli":[2,29,30],"applic":[0,26],"applicationrequir":21,"archiv":21,"argument":[12,29],"argv":12,"around":[20,25],"arriv":[14,24],"asc":[24,28,29],"assert":18,"assign":21,"associ":[2,30],"async":[24,25,29],"atom":24,"attach":24,"attempt":[4,24],"attribut":[1,4,21,22,23,24,25,26,28,30],"attributemetadata":22,"attributetyp":[21,30],"attributetypenam":21,"auth":[1,24],"authent":[1,6],"author":7,"auto":[21,26,28,33],"autoapi":33,"autocomplet":19,"automat":[1,4,7,14,23,26,29],"avail":[19,20,24,28],"avg":28,"avoid":25,"azur":1,"b":29,"babbag":24,"back":[24,29],"backoff":4,"backup":7,"backup_count":7,"backward":[20,22,30],"base":[5,15,18,19,26],"base_url":[1,25,26,28,29,30],"basenam":26,"basic":[28,30],"batch":[1,12,14,16,25,27,33],"batchdataframeoper":24,"batchitemrespons":[13,24],"batchoper":24,"batchqueryoper":24,"batchrecordoper":24,"batchrequest":24,"batchresult":[13,24],"batchtableoper":24,"becaus":[15,24,28],"becom":[24,25],"befor":[5,7,14,19,24],"behavior":[21,30],"behaviour":24,"besid":25,"beta":12,"bind":[24,28],"block":14,"bob":24,"bodi":[5,7,13,24],"body_excerpt":5,"bool":[5,12,13,19,22,24,25,26,28,29,30],"boolean":30,"bound":[15,19,24,28],"break":[1,12],"broad":28,"broadcast":[24,25,29],"build":[15,19,28,30],"builder":[12,16,19,24,28],"builder_chain":12,"bulk":[24,29],"bulk_id":13,"bulkdelet":[24,25,29],"bundl":24,"busi":7,"by_pag":[12,19],"byte":7,"c":[25,28],"cach":1,"call":[1,5,12,14,19,24,25,28],"caller":[1,4,12,24],"can":[15,17,19,21,28],"canbechang":21,"capabl":[1,8],"captur":7,"cascad":[2,21,24,30],"cascade_behavior_cascad":2,"cascade_behavior_no_cascad":2,"cascade_behavior_remove_link":[2,24,30],"cascade_behavior_restrict":2,"cascade_configur":[21,30],"cascade_delet":[24,30],"cascadeconfigur":[21,30],"case":[7,15,19,24,25,28,29,30],"categori":5,"caus":30,"ch1":29,"ch2":29,"chain":[12,19,28],"chang":[12,20,24,25,29],"changeset":[13,24],"changesetrecordoper":24,"charact":4,"check":[15,24],"child":[21,24,30],"chunk":[1,26],"class":27,"classmethod":[4,18,20,21,22],"claud":0,"claude_skil":[10,33],"claus":30,"cleanup":1,"clear":[1,24,25],"clear_nul":[24,25],"client":[0,4,5,6,10,12,13,14,18,19,20,21,22,24,25,26,28,29,30,33],"client_request_id":5,"client_var":12,"close":[1,4],"closest":15,"cls":18,"cnt":[25,28],"coalesc":28,"code":[0,5,13,17,20,22,24,30],"codemod":12,"col":[12,15,16,19,28,29,30],"col1":19,"col2":19,"collect":[15,19,20,28,29],"column":[1,2,13,15,19,22,24,25,26,28,29,30],"columninfo":22,"columnproxi":15,"columns_cr":[22,30],"com":[1,25],"combin":[24,30],"comment":[4,12],"common":[10,28,30,33],"communic":1,"communiti":[24,25,29],"comparison":15,"compat":[20,22,30],"compil":[15,19],"complex":[15,19],"compon":6,"compos":[15,16,19,22,28,30],"composit":[15,29],"concret":12,"concurr":20,"condit":15,"config":[1,6,21,33],"configur":[1,4,6,7,21,29],"connect":1,"consid":25,"consolid":[19,25],"constant":[3,30,33],"constraint":19,"construct":[1,15,19,24,28],"constructor":4,"contact":[21,24,25,28,29],"contactid":[25,28],"contain":[0,3,4,5,6,7,8,12,13,15,19,24,25,27,28,29,33],"content_id":13,"context":[1,4,5,24],"continu":[20,24],"continue_on_error":24,"contoso":[1,15,23,24,25,29],"contract":26,"contribut":[0,13,24],"control":4,"conveni":[4,24,30],"convers":[1,24],"convert":[5,17,18,21,24,25],"copi":20,"core":[1,10,33],"correl":5,"correlation_id":5,"correspond":[22,24,29],"count":[19,24,25,28,29],"counterpart":29,"creat":[1,4,7,12,13,18,19,20,21,22,24,25,26,28,29,30,33],"create_alternate_key":30,"create_column":12,"create_lookup_field":[21,24,30],"create_many_to_many_relationship":[21,24,30],"create_one_to_many_relationship":[21,24,30],"create_t":12,"createdon":[15,19,29],"createmultipl":[13,24,25,29],"creation":[22,24,29,30],"credenti":[1,25,26,28,29,30],"crm":[1,2,15,17,21,30],"cross":28,"crud":[1,8,18,25,29],"cs":24,"cs_intern":24,"cte":28,"current":[1,9,26],"custom":[1,30],"custom_t":30,"d":12,"data":[7,10,13,16,18,20,22,23,24,29,33],"dataclass":[16,18],"datafram":[1,12,19,20,24,27,33],"dataframeoper":25,"datavers":[32,33],"dataverse_20260310_143022":7,"dataverse_log":7,"dataversecli":[1,7,14,24,25,26,28,29,30],"dataverseconfig":[1,4,7],"dataverseerror":5,"dataversemodel":[16,18],"date":[28,30],"datetim":30,"debug":7,"decim":[22,30],"def":18,"default":[1,4,7,17,19,20,21,22,24,25,26,28,29,30],"defin":[1,2,4,21,29,30],"definit":[16,22,30],"deleg":[1,25],"delet":[1,2,12,13,21,24,25,28,29,30],"delete_alternate_key":30,"delete_column":12,"delete_relationship":[24,30],"delete_t":12,"depart":30,"depend":29,"deprec":[12,15,19,24],"depth":28,"desc":[28,29],"descend":[19,28],"descript":[12,21,22,24,29,30],"detail":5,"detect":21,"determin":28,"dev":0,"develop":[0,12],"df":[14,19,24,25,28],"diagnost":[4,5,7],"dict":[5,13,17,18,20,21,22,23,24,28,29,30],"dictionari":[5,18,19,22,23,29,30],"differ":25,"direct":[18,21,24,28],"directori":7,"disabl":7,"discov":28,"discover":19,"dispatch":[18,24],"display":[21,22,24,25,28,29,30],"display_nam":[21,22,24,30],"displaynam":[21,30],"distinct":28,"distribut":5,"docstr":1,"document":[1,33],"doe":[4,25,26,28,29,30],"doesn":26,"domain":1,"doubl":30,"downstream":14,"dri":12,"dry_run":12,"dv":12,"dynam":[1,2,15,17,21,30],"e":[1,5,7,13,17,18,20,21,22,24,25,26,28,29,30],"eager":[19,28,29],"earli":14,"either":[5,21,29],"element":[14,24,28,29],"els":13,"email":[4,26],"email_id":26,"employe":30,"empti":[1,4,15,19,20,24,25,26,28,29,30],"enabl":[1,7,18],"end":28,"endpoint":28,"endswith":[12,15],"english":[4,17,30],"enqueu":24,"entiti":[13,14,16,18,19,20,21,22,24,28,29,30],"entity1_logical_nam":[21,30],"entity1logicalnam":21,"entity1navigationpropertynam":21,"entity2_logical_nam":[21,30],"entity2logicalnam":21,"entity_id":13,"entity_nam":14,"entity_set":24,"entity_set_nam":[22,30],"entitydefinit":[22,24,30],"entityid":13,"entitykeymetadata":22,"entityset":[1,28],"entitysetnam":30,"entri":[1,13,24,28,29],"enum":[24,30],"environ":[1,30],"eq":[15,19,24,25,29,30],"equal":[15,29],"equival":[12,15,24],"err":13,"error":[4,6,13,24,33],"error_cod":13,"error_messag":13,"escap":[15,23],"etag":20,"etc":[7,21,24],"everi":[7,14,29,30],"exact":[24,25,28,29,30],"exampl":[1,13,14,15,17,18,19,20,21,22,23,24,25,26,28,29,30],"exceed":24,"except":1,"excerpt":5,"exclud":[20,28],"execut":[1,12,13,14,19,20,24,25,28,29],"execute_pag":[12,14,19,28],"exist":[2,20,21,24,25,26,29,30],"exit":14,"expand":[13,19,24,25,28,29],"expandopt":19,"expans":19,"expect":29,"explicit":[20,25,26,28],"expr":[12,15],"expr1":15,"expr2":15,"express":[12,15,16,19,24,28,29,30],"extens":[10,33],"extract":[13,20],"f":[1,13,25,28,29,30],"fabrikam":[24,25,29],"fail":[12,13,22,24,26,30],"failur":[5,24],"fals":[5,12,19,22,24,25,26,28,29,30],"feed":24,"fetch":[14,25,28,29],"fetchxml":[14,16,28],"fetchxml_queri":[16,28,33],"fetchxmlqueri":[14,16,28],"field":[20,21,24,25,28,29,30],"file":[1,4,7,8,12,27,30,33],"file_column":26,"filenam":[7,26],"filenotfounderror":26,"fileoper":26,"filesystem":26,"filter":[12,16,19,24,25,28,29,30,33],"filter_":12,"filter_between":12,"filter_contain":12,"filter_endswith":12,"filter_eq":12,"filter_g":12,"filter_gt":12,"filter_in":[12,15],"filter_l":12,"filter_lt":12,"filter_n":12,"filter_not_between":12,"filter_not_in":12,"filter_not_nul":12,"filter_nul":12,"filter_raw":12,"filter_startswith":12,"filter_str":[15,19],"filterexpress":[15,24,29],"final":1,"find":14,"find_manual_pattern":12,"fire":14,"first":[1,20,21,24],"firstnam":[24,28],"flag":[12,19,28],"flat":[12,19,20],"float":[4,30],"fluent":[15,16,19,28],"fluentli":28,"flush":1,"flush_cach":1,"folder":7,"follow":[19,29],"form":[4,14,24,28],"format":[4,17,21,22],"formattedvalu":[24,25,29],"found":[23,24,25,28,29,30],"foundat":6,"free":4,"from_api_respons":[20,21,22],"from_dict":[18,22],"from_env":[1,4],"from_many_to_mani":21,"from_one_to_mani":21,"from_tabl":28,"frozenset":7,"full":[19,28],"fullnam":[24,28,29],"function":[8,28],"futur":[1,4,18,19,24,31],"g":[1,5,7,13,17,18,20,21,22,24,25,26,28,29,30],"ga":[12,15,24,29],"ge":15,"generat":[5,21,29,33],"get":[1,12,13,18,19,20,22,24,25,28,29,30],"get_alternate_key":30,"get_relationship":[21,24,30],"get_table_info":12,"given":30,"got":25,"greater":15,"group":[24,25,27,28],"gt":15,"guard":12,"guid":[13,20,21,22,23,24,25,26,28,29,30],"guidanc":0,"hand":18,"handl":[1,6,8,12,22],"has_error":13,"hatch":15,"head":25,"header":[1,4,5,7,13,24,25,26,29],"help":[5,19],"helper":[16,18],"hi":[12,15],"hierarchi":5,"high":[1,15],"hint":[24,29],"hold":14,"http":[1,4,5,6,7,13,14,19,24,28,29],"http_backoff":4,"http_error":5,"http_retri":4,"http_timeout":4,"httperror":[1,5,24,26,30],"https":[1,25],"human":[5,22,24,30],"id":[4,5,12,13,20,24,25,29,30],"id1":[24,29],"id2":[24,29],"id3":29,"id_column":[24,25],"ide":19,"ident":1,"identifi":[2,5,23,25,29,30],"idiom":15,"ids_seri":24,"if_none_match":26,"ignor":24,"immedi":[1,24],"immut":4,"implement":18,"import":[1,12,15,18,19,24,25,28,29,30],"importerror":20,"importsequencenumb":28,"in_":[12,15],"inact":30,"inc":[24,29],"includ":[6,7,13,17,21,23,24,25,28,29,30],"include_annot":[19,24,25,29],"include_system":28,"index":[22,25,30],"individu":[1,13,24,29],"inert":[14,28],"info":[22,30],"inform":5,"infrastructur":6,"inherit":21,"initi":[1,5,19],"inject":5,"inner":28,"inprogress":22,"input":[21,24,25],"insensit":[7,15],"insert":[12,28],"insid":24,"inspect":[1,30],"instal":[12,20],"instanc":[4,15,17,18,19,24,25,26,28,29,30],"instanti":24,"instead":[1,15,19,24,28,29,30],"int":[1,4,5,7,12,13,17,19,22,24,25,29,30],"integ":[1,22,23,30],"integr":18,"intend":4,"intent":[12,25],"intenum":30,"interact":1,"interactivebrowsercredenti":1,"interfac":[1,19],"intern":[1,22,24,25],"intersect":[21,30],"intersect_entity_nam":21,"intersectentitynam":21,"invalid":[24,25],"irrevers":30,"is_not_nul":[12,15],"is_nul":[12,15],"is_pk":28,"is_primari":22,"is_requir":22,"is_success":13,"is_transi":5,"iscustomiz":21,"isinst":18,"ispriv":[24,30],"issecur":21,"isvalidforadvancedfind":21,"item":[13,16,20,22,23,24,29],"itemstatus":30,"itemsview":20,"iter":[12,14,16,19,20,25,29],"jane":28,"job":[24,25,29],"job_id":29,"jobid":24,"join":[25,28],"json":[13,17,20,21,24],"just":28,"keep":[7,21],"key":[1,4,20,21,22,23,24,28,29,30],"key_attribut":[22,30],"key_id":30,"key_nam":30,"keysview":[20,22],"keyword":15,"kind":1,"known":20,"kw":12,"label":[1,2,4,16,21,23,24,28,30,33],"languag":[1,17,24,30],"language_cod":[4,17,24,30],"languagecod":17,"larg":[14,25],"large_fil":26,"last":[17,21],"lastnam":28,"layer":[8,22],"lazi":[19,28],"lazili":[1,14,19,29],"lcid":[4,17],"le":15,"lead":24,"lead_ref":24,"least":19,"left":[25,28],"legaci":[22,30],"legacy_key":22,"len":[13,25,29],"length":[22,29],"less":15,"let":24,"level":[1,7,12,21,24,25,29,30],"libcst":12,"lifecycl":1,"lightweight":[1,4],"like":[15,19,20,21,22,28,30],"limit":[19,24,25,28],"link":[21,28],"list":[12,13,17,19,20,22,24,25,28,29,30],"list_column":30,"list_pag":29,"list_relationship":30,"list_tabl":12,"list_table_relationship":30,"liter":12,"lo":[12,15],"load":1,"local":[4,7,17,26,30],"localized_label":[17,30],"localizedlabel":[2,17,21,30],"log":[4,7],"log_config":[4,6,33],"log_file_prefix":7,"log_fold":7,"log_level":7,"logconfig":[4,7],"logic":[15,18,21,22,24,25,27,28,29,30],"logical_nam":[22,30],"logicalnam":[21,30],"lookup":[21,24,28,30],"lookup_attribut":28,"lookup_field_nam":[24,30],"lookup_schema_nam":[21,30],"lookupattributemetadata":[2,21,24,30],"lookuptyp":21,"low":15,"lowercas":[15,22,23,25,28,29],"lt":15,"ltd":[1,23,24,29],"m":[12,28],"made":[14,28],"main":12,"make":25,"manag":[1,8,12,24,30],"mani":[21,24,30],"manual":[1,12,18,19,24,28],"manual_review_not":12,"many_to_mani":21,"manytomanyrelationship":30,"manytomanyrelationshipmetadata":[2,21,24,30],"manytoonerelationship":30,"map":[21,23,24,29,30],"mark":1,"match":[4,14,15,19,23,24,25,26,28,29],"math":28,"max":[7,28],"max_body_byt":7,"max_file_byt":7,"max_length":22,"maximum":[4,7,19,22,24,25,29],"maxpages":[24,29],"may":[1,5,7,14,29,30],"mb":7,"mechan":12,"medium":14,"memo":30,"memori":14,"merg":[17,21,28],"messag":[4,5,13],"metadata":[1,2,5,8,17,20,21,22,24,28,29,30],"metadata_id":[22,30],"metadataerror":[5,24,30],"metadataid":24,"method":[1,12,15,18,19,24,25,28,29,30],"microsoft":[1,2,15,17,21,30],"migrat":[10,33],"migrate_fil":12,"migrate_sourc":12,"migrate_v0_to_v1":[11,33],"mime":26,"mime_typ":26,"min":28,"mirror":[15,24],"miss":[1,7,24,25,28],"mode":[20,24,26],"model":[10,12,24,28,29,30,33],"modul":[3,6,8,27,31],"money":30,"ms":5,"multi":[24,28,29],"multilin":30,"multipl":[1,13,17,24,25,29],"multipli":4,"must":[19,24,25,29,30],"mysolut":30,"n":[28,30],"name":[1,7,12,14,15,18,19,20,21,22,23,24,25,26,28,29,30],"namespac":[1,12,24,25,26,27,28,29,30],"nan":[24,25],"narrow":[24,30],"nav":[19,28],"nav_properti":28,"navig":[19,24,25,28,29],"navprop":28,"ne":15,"need":[14,28],"negat":15,"neither":[24,29],"nest":[19,28],"network":[1,19,29],"never":25,"new":[13,22,24,29,30],"new_":30,"new_account_contact":21,"new_account_ord":21,"new_accountid":[21,30],"new_act":30,"new_attach":26,"new_contract":26,"new_depart":30,"new_department_employe":30,"new_departmentid":30,"new_df":25,"new_docu":26,"new_employe":30,"new_employee_project":30,"new_instock":30,"new_mytestt":[25,26,29,30],"new_not":30,"new_ord":[21,30],"new_pric":[22,30],"new_product":[22,24,30],"new_product_code_key":30,"new_productcod":30,"new_productnam":30,"new_project":30,"new_rat":30,"new_status":30,"new_testcolumn":30,"new_titl":30,"newli":30,"next":28,"nextlink":[19,24,29],"nocascad":[2,21],"non":[1,13,15,23,24,25,29,30],"none":[1,4,5,12,13,17,18,20,21,22,24,25,26,29,30],"not_between":[12,15],"not_in":[12,15],"not_lik":15,"notin":15,"now":18,"null":[15,24,25,26,28],"number":[1,4,5,7,19,24,25,29],"object":[1,4,14,16,20,28,29,30],"obtain":[14,24],"octet":26,"odata":[1,2,8,13,15,16,17,18,19,20,21,22,23,24,25,28,29,30],"odata_bind":28,"odata_expand":28,"odata_select":28,"odata_type_label":2,"odata_type_localized_label":2,"odata_type_lookup_attribut":2,"odata_type_many_to_many_relationship":2,"odata_type_one_to_many_relationship":2,"offset":28,"ok":13,"omit":[24,29,30],"onc":[14,19,29,30],"one":[13,14,19,21,23,24,25,28,29,30],"one_to_mani":[21,30],"onetomanyrelationship":30,"onetomanyrelationshipmetadata":[2,21,24,30],"onli":[1,4,7,19,21,24,25,26,28,29,30],"oper":[1,2,4,5,8,10,13,15,16,18,21,23,33],"operation_context":4,"operationcontext":[1,4],"opt":[7,19],"optimist":20,"option":[1,4,5,9,17,19,21,23,24,25,29,30],"order":[13,24,25,28],"order_bi":[19,28],"orderbi":[19,24,25,29],"org":1,"organiz":[1,27],"orient":[24,25],"origin":5,"originatingleadid":24,"otherwis":[19,29],"outbound":[1,4,5],"outgo":28,"output":21,"overload":15,"overrid":[17,21],"overriddencreatedon":28,"overwrit":26,"packag":0,"page":[12,14,19,20,24,25,28,29,33],"page_s":[19,24,25,28,29],"pagin":[1,20,24,25,29],"pair":[4,20,22,24,29],"panda":[1,19,20,24,25],"param":19,"paramet":[1,4,5,7,13,14,15,17,19,20,21,22,23,24,25,26,28,29,30],"parent":[14,21,24,25,26,28,29,30],"parentaccountid":15,"parentcustomerid":[25,28],"parentcustomerid_account":28,"parenthes":[4,28],"pars":[5,12,13],"part":13,"partner":29,"pascalcas":[21,22,28,30],"pass":[1,15,18,19,20,25,28,29,30],"patch":[13,24,25,29],"path":[7,12,26],"pathlib":12,"pattern":[12,15,20,24,25,29],"payload":[2,17,18,21,23,28],"pd":[24,25],"pdf":26,"pend":[22,30],"per":[7,13,14,19,24,25,29],"perform":[2,21,24,29],"permit":24,"picklist":[1,23],"pii":[4,7],"pip":12,"pk":28,"place":12,"placehold":[9,31],"plain":[18,19,20,24,29],"plugin":[1,4],"plus":[4,28],"point":28,"pool":1,"posit":[13,29],"post":[13,24],"potenti":5,"powerplatform":33,"pre":24,"prefer":[14,15,24,25,29],"prefix":[7,30],"present":[20,22],"preserv":[12,20],"prevent":[2,19,21,24],"preview":12,"primari":[21,22,23,24,28,29,30],"primary_column":[24,30],"primary_id_attribut":22,"primary_name_attribut":22,"primarycontactid":[24,29],"primaryid":1,"print":[1,13,15,19,20,21,22,24,25,28,29,30],"prior":24,"privat":30,"process":[14,19,28,29],"process_batch":19,"produc":[15,24,25],"product":30,"progress":14,"project":[24,30],"properti":[5,13,17,19,21,24,25,28,29,30],"propertynam":15,"propertyvalu":15,"protocol":[1,8,16,33],"provid":[1,4,5,7,15,16,19,21,24,25,26,28,29,30],"proxi":[7,15],"public":13,"pydant":18,"python":[1,7,12,15],"queri":[1,5,8,12,14,16,19,20,24,25,27,29,30,33],"query_build":[16,28,33],"query_sql":12,"querybuild":[12,15,16,19,28],"queryoper":[14,28],"queryparam":19,"queryresult":[12,14,16,19,20,29],"queu":30,"quot":23,"r":[20,28],"rais":[1,4,5,12,15,19,20,21,24,25,26,28,29,30],"rather":23,"raw":[12,15,16,19,20,21,22,30],"read":[1,28],"readabl":[5,22,24,30],"readi":[24,30],"rec":12,"receiv":[12,24],"recogn":21,"recognis":12,"recommend":[1,19,21,28],"reconstruct":[12,18],"record":[1,2,12,13,14,16,18,19,21,23,24,25,26,27,28,30,33],"record_id":[1,20,24,25,26,29],"recordoper":29,"redact":[4,7],"redacted_head":7,"reduc":15,"ref":24,"refer":[21,24,34],"referenc":[2,21,30],"referenced_attribut":[21,30],"referenced_ent":[21,30],"referenced_t":[24,30],"referencedattribut":21,"referencedent":[21,30],"referencing_attribut":21,"referencing_ent":[21,30],"referencing_t":[24,30],"referencingent":[21,30],"reject":4,"rel":[21,30],"relat":[19,21],"relationship":[16,24,28,30,33],"relationship_id":[21,24,30],"relationship_schema_nam":[21,30],"relationship_typ":[21,30],"relationshipinfo":[21,30],"relationshiptyp":30,"releas":[1,18,19,24],"reli":24,"remain":14,"remov":[1,2,12,19,21,24,30],"remove_column":[12,24,30],"removelink":[2,21,30],"renam":12,"repar":21,"replac":[7,12,15,24,29],"report":14,"repres":[17,23,24,25],"represent":5,"request":[1,4,5,7,13,14,19,20,24,25,28,29,30],"requir":[4,12,18,21,22,24,30],"required_level":21,"requiredlevel":21,"reserv":4,"resolut":24,"resolv":[23,24],"resourc":1,"resp":13,"respons":[1,4,5,7,13,18,20,21,22,24,25,29,30],"response_data":[20,21,22],"restrict":[2,21,24,30],"result":[12,13,14,16,19,20,21,24,25,28,29,30],"retri":[1,4,5],"retriev":[12,22,24,25,29,30],"retry_aft":5,"return":[1,4,5,12,13,14,15,18,19,20,21,22,24,25,28,29,30],"revenu":[15,19,25,28],"review":12,"rewrit":12,"rewritten":12,"right":28,"roll":[18,24],"rollupview":21,"root":28,"rotat":7,"round":[14,24],"row":[14,24,25,28],"rule":15,"run":12,"runtime_check":18,"runtimeerror":[1,19],"s":[1,13,24,29],"safe":[1,19],"save":18,"scan":19,"scenario":28,"schema":[14,19,20,21,22,24,25,26,28,29,30],"schema_nam":[21,22,24,30],"schemanam":[21,30],"scienc":24,"script":12,"sdk":[0,3,5,6,7,8,9,12,15,16,22,23,24,25,26,27,28,29,30,31],"search":[1,28],"second":[4,5,21],"section":12,"securitytyp":21,"see":[5,12,19],"select":[12,19,20,24,25,26,28,29,30],"self":[5,18,19],"semicolon":4,"send":[24,26],"sensit":[4,7,19,24,25,29],"sent":[24,25,29],"separ":4,"sequenti":[24,25,29],"seri":[24,25],"server":[5,24,25,29],"servic":[5,13],"service_error_cod":5,"service_request_id":5,"session":[1,7],"set":[4,13,14,18,19,21,22,23,24,25,26,28,30],"share":[3,21],"shortcut":12,"shot":[14,19,29],"side":[5,29],"signatur":[1,5,19,24],"silent":[12,25],"simpl":[1,14,30],"simplifi":28,"sinc":[15,19,24],"singl":[1,13,15,19,20,23,24,25,28,29,30],"site":12,"size":[7,24,25,26,29],"skill":[0,4],"skip":[14,24,25],"slash":1,"small":[14,26],"smaller":25,"snake_cas":[21,22],"solut":[24,30],"sort":[19,24,25,29],"sourc":[5,12,28],"special":5,"specif":[1,5,17,19,24,25,30],"specifi":[7,18,19,21,24,25,26,28,30],"sphinx":33,"split":25,"sql":[1,5,8,12,20,24,25,28],"sql_column":28,"sql_join":12,"sql_select":12,"sqlparseerror":5,"stabl":1,"standalon":19,"standard":24,"start":30,"startswith":[12,15,30],"state":[1,4],"statecod":[15,19,24,25,28,29],"statement":[24,25,28],"status":[5,13,22,29,30],"status_cod":[5,13],"statuscod":[24,29],"stop":[14,24],"store":26,"str":[1,4,5,7,12,13,14,15,17,18,19,20,21,22,23,24,25,26,28,29,30],"strategi":26,"stream":[14,26,29],"string":[4,14,15,19,20,22,23,24,25,28,29,30],"strip":[14,20],"strong":20,"structur":[5,18,19,20],"style":15,"sub":30,"subcategori":5,"subclass":[24,30],"subcod":5,"subject":19,"submiss":[13,24],"submit":[13,24],"subqueri":28,"subsequ":24,"subset":[24,28],"succeed":[5,13,24,26],"success":[1,13],"suffix":28,"suitabl":28,"sum":28,"suppli":[24,30],"support":[1,15,18,21,22,24,25,28,29,30],"svc":12,"sync":19,"syntax":[12,19],"system":28,"t":[12,26,28],"tabl":[1,2,12,18,19,20,21,22,24,25,26,27,28,29,33],"table_info":[16,30,33],"table_logical_nam":30,"table_schema_nam":[22,30],"tableinfo":[22,30],"tableoper":30,"target":[21,23,24,26,28],"target_entity_set":28,"target_id":28,"target_t":28,"task":19,"telephone1":[1,18,19,23,24,25,29],"text":[4,17,30],"three":[13,29],"time":[1,24,28,29],"timeout":[1,4,25],"timestamp":[4,5,7],"timezoneruleversionnumb":28,"to_datafram":[12,14,19,20,28,29],"to_dict":[5,17,18,20,21,22],"to_odata":[15,19],"to_tabl":28,"today":[15,18],"togeth":[1,24],"tokencredenti":1,"tool":[1,4],"top":[12,19,24,25,28,29],"total":[25,29],"trace":[5,7],"tracepar":5,"track":5,"trail":1,"transact":24,"transient":[4,5],"transit":30,"transpar":24,"tree":[12,15,19,28],"tri":1,"trigger":[19,29],"trim":1,"trip":[14,24],"true":[12,13,19,21,24,25,26,28,29,30],"tune":4,"tupl":[12,22],"two":[0,21,29],"type":[1,2,4,5,12,13,14,15,16,17,18,19,20,21,22,24,25,26,28,29,30],"typeddict":19,"typeerror":[24,25,29],"typic":[5,21,30],"unchang":[15,25],"understand":12,"union":28,"uniqu":[24,30],"unit":4,"unknown":4,"unrecogn":21,"unshar":21,"unsupport":24,"updat":[1,12,13,18,24,25,28,29],"updatemultipl":[24,25],"upfront":14,"upload":[1,8,12,26],"upload_fil":12,"upsert":[16,24,29,30,33],"upsertitem":[16,23,24,29],"upsertmultipl":[13,24,29],"uri":24,"url":[1,23,29],"usabl":[24,28],"usag":[12,29],"use":[0,1,2,3,4,12,14,15,19,20,21,23,24,25,26,28,29,30],"use_bulk_delet":[24,25,29],"user":[1,4,17],"user_agent_context":4,"user_localized_label":17,"userlocalizedlabel":17,"usual":21,"utcconversiontimezonecod":28,"util":[3,10,33],"v":12,"v0":12,"v1":[12,24,25,29],"val":12,"valid":[4,5,21],"validation_error":5,"validationerror":[5,24,25,28],"valu":[1,2,4,5,7,13,15,17,20,21,22,23,24,25,28,29,30],"valueerror":[1,4,15,19,21,24,25,28,29],"valuesview":20,"var":12,"variabl":[12,18],"verbatim":15,"veri":25,"version":[15,17,19,24],"versionnumb":28,"via":[1,7,12,13,14,16,19,24,25,26,28,29,30],"virtual":28,"vs":21,"w3c":5,"wait":5,"want":[14,25],"warn":15,"was_chang":12,"web":[1,2,5,17,21,22,28,30],"websiteurl":25,"well":[14,28,30],"whether":[5,22,24,30],"whitespac":[12,25],"whose":[7,30],"will":[1,12,15,18,19,24,26,30],"window":28,"within":[5,13,24],"without":[1,12,18,20,23,24,25],"work":[18,20],"wrap":[20,30],"wrapper":[1,16,20,24,25],"write":[7,12,14,24,28],"written":22,"x":5,"xml":[14,28],"yet":18,"yield":[14,19,29],"zip":26},"titles":["PowerPlatform.Dataverse.claude_skill","PowerPlatform.Dataverse.client","PowerPlatform.Dataverse.common.constants","PowerPlatform.Dataverse.common","PowerPlatform.Dataverse.core.config","PowerPlatform.Dataverse.core.errors","PowerPlatform.Dataverse.core","PowerPlatform.Dataverse.core.log_config","PowerPlatform.Dataverse.data","PowerPlatform.Dataverse.extensions","PowerPlatform.Dataverse","PowerPlatform.Dataverse.migration","PowerPlatform.Dataverse.migration.migrate_v0_to_v1","PowerPlatform.Dataverse.models.batch","PowerPlatform.Dataverse.models.fetchxml_query","PowerPlatform.Dataverse.models.filters","PowerPlatform.Dataverse.models","PowerPlatform.Dataverse.models.labels","PowerPlatform.Dataverse.models.protocol","PowerPlatform.Dataverse.models.query_builder","PowerPlatform.Dataverse.models.record","PowerPlatform.Dataverse.models.relationship","PowerPlatform.Dataverse.models.table_info","PowerPlatform.Dataverse.models.upsert","PowerPlatform.Dataverse.operations.batch","PowerPlatform.Dataverse.operations.dataframe","PowerPlatform.Dataverse.operations.files","PowerPlatform.Dataverse.operations","PowerPlatform.Dataverse.operations.query","PowerPlatform.Dataverse.operations.records","PowerPlatform.Dataverse.operations.tables","PowerPlatform.Dataverse.utils","PowerPlatform","API Reference","PowerPlatform Dataverse Client SDK"],"titleterms":{"api":33,"appli":12,"attribut":2,"batch":[13,24],"class":[1,4,7,13,14,15,17,18,19,20,21,22,23,24,25,26,28,29,30],"claude_skil":0,"client":[1,34],"common":[2,3],"config":4,"constant":2,"content":[1,2,4,5,7,12,13,14,15,17,18,19,20,21,22,23,24,25,26,28,29,30,34],"core":[4,5,6,7],"data":8,"datafram":25,"datavers":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,34],"error":5,"except":5,"extens":9,"fetchxml_queri":14,"file":26,"filter":15,"function":[12,15],"label":17,"log_config":7,"migrat":[11,12],"migrate_v0_to_v1":12,"model":[13,14,15,16,17,18,19,20,21,22,23],"modul":[1,2,4,5,7,12,13,14,15,17,18,19,20,21,22,23,24,25,26,28,29,30],"oper":[24,25,26,27,28,29,30],"powerplatform":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,34],"protocol":18,"queri":28,"query_build":19,"record":[20,29],"refer":33,"relationship":21,"sdk":34,"submodul":[3,6,10,11,16,27,32],"tabl":30,"table_info":22,"transform":12,"upsert":23,"util":31}}) \ No newline at end of file diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/claude_skill/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/claude_skill/index.rst deleted file mode 100644 index 88631626..00000000 --- a/docs_local/autoapi/PowerPlatform/Dataverse/claude_skill/index.rst +++ /dev/null @@ -1,15 +0,0 @@ -PowerPlatform.Dataverse.claude_skill -==================================== - -.. py:module:: PowerPlatform.Dataverse.claude_skill - -.. autoapi-nested-parse:: - - Claude Code skill package for the PowerPlatform Dataverse Client SDK. - - This package contains two skills: - - dataverse-sdk-use: Guidance for using the SDK in your applications - - dataverse-sdk-dev: Guidance for developing/contributing to the SDK itself - - - diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/client/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/client/index.rst deleted file mode 100644 index 3e3c485f..00000000 --- a/docs_local/autoapi/PowerPlatform/Dataverse/client/index.rst +++ /dev/null @@ -1,157 +0,0 @@ -PowerPlatform.Dataverse.client -============================== - -.. py:module:: PowerPlatform.Dataverse.client - - -Classes -------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.client.DataverseClient - - -Module Contents ---------------- - -.. py:class:: DataverseClient(base_url: str, credential: azure.core.credentials.TokenCredential, config: Optional[PowerPlatform.Dataverse.core.config.DataverseConfig] = None, *, context: Optional[PowerPlatform.Dataverse.core.config.OperationContext] = None) - - High-level client for Microsoft Dataverse operations. - - This client provides a simple, stable interface for interacting with Dataverse environments - through the Web API. It handles authentication via Azure Identity and delegates HTTP operations - to an internal OData client. - - Key capabilities: - - OData CRUD operations: create, read, update, delete records - - SQL queries: execute read-only SQL via Web API ``?sql`` parameter - - Table metadata: create, inspect, and delete custom tables; create and delete columns - - File uploads: upload files to file columns with chunking support - - :param base_url: Your Dataverse environment URL, for example - ``"https://org.crm.dynamics.com"``. Trailing slash is automatically removed. - :type base_url: :class:`str` - :param credential: Azure Identity credential for authentication. - :type credential: ~azure.core.credentials.TokenCredential - :param config: Optional configuration for language, timeouts, and retries. - If not provided, defaults are loaded from :meth:`~PowerPlatform.Dataverse.core.config.DataverseConfig.from_env`. - :type config: ~PowerPlatform.Dataverse.core.config.DataverseConfig or None - :param context: Optional caller-defined context object appended to the - outbound ``User-Agent`` header for plugin/tool attribution. Cannot be used - together with ``config`` -- pass the context via - :class:`~PowerPlatform.Dataverse.core.config.DataverseConfig` instead. - :type context: ~PowerPlatform.Dataverse.core.config.OperationContext or None - - :raises ValueError: If ``base_url`` is missing or empty after trimming. - :raises ValueError: If both ``config`` and ``context`` are provided. - - .. note:: - The client lazily initializes its internal OData client on first use, allowing lightweight construction without immediate network calls. - - .. note:: - All methods that communicate with the Dataverse Web API may raise - :class:`~PowerPlatform.Dataverse.core.errors.HttpError` on non-successful - HTTP responses (e.g. 401, 403, 404, 429, 500). Individual method - docstrings document only domain-specific exceptions. - - Operations are organized into namespaces: - - - ``client.records`` -- create, update, delete, and get records (single or paginated queries) - - ``client.query`` -- query and search operations - - ``client.tables`` -- table and column metadata management - - ``client.files`` -- file upload operations - - ``client.dataframe`` -- pandas DataFrame wrappers for record CRUD - - ``client.batch`` -- batch multiple operations into a single HTTP request - - The client supports Python's context manager protocol for automatic resource - cleanup and HTTP connection pooling: - - .. rubric:: Example - - **Recommended -- context manager** (enables HTTP connection pooling):: - - from azure.identity import InteractiveBrowserCredential - from PowerPlatform.Dataverse.client import DataverseClient - - credential = InteractiveBrowserCredential() - - with DataverseClient("https://org.crm.dynamics.com", credential) as client: - record_id = client.records.create("account", {"name": "Contoso Ltd"}) - client.records.update("account", record_id, {"telephone1": "555-0100"}) - # Session closed, caches cleared automatically - - **Manual lifecycle**:: - - client = DataverseClient("https://org.crm.dynamics.com", credential) - try: - record_id = client.records.create("account", {"name": "Contoso Ltd"}) - finally: - client.close() - - - .. py:attribute:: auth - - - .. py:attribute:: records - - - .. py:attribute:: query - - - .. py:attribute:: tables - - - .. py:attribute:: files - - - .. py:attribute:: dataframe - - - .. py:attribute:: batch - - - .. py:method:: close() -> None - - Close the client and release resources. - - Closes the HTTP session (if any), clears internal caches, and - marks the client as closed. Safe to call multiple times. After - closing, any operation will raise :class:`RuntimeError`. - - Called automatically when using the client as a context manager. - - Example:: - - client = DataverseClient(base_url, credential) - try: - client.records.create("account", {"name": "Contoso"}) - finally: - client.close() - - - - .. py:method:: flush_cache(kind) -> int - - Flush cached client metadata or state. - - :param kind: Cache kind to flush. Currently supported values: - - - ``"picklist"``: Clears picklist label cache used for label-to-integer conversion - - Future kinds (e.g. ``"entityset"``, ``"primaryid"``) may be added without - breaking this signature. - :type kind: :class:`str` - - :return: Number of cache entries removed. - :rtype: :class:`int` - - .. rubric:: Example - - Clear the picklist cache:: - - removed = client.flush_cache("picklist") - print(f"Cleared {removed} cached picklist entries") - - - diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/common/constants/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/common/constants/index.rst deleted file mode 100644 index b843380a..00000000 --- a/docs_local/autoapi/PowerPlatform/Dataverse/common/constants/index.rst +++ /dev/null @@ -1,77 +0,0 @@ -PowerPlatform.Dataverse.common.constants -======================================== - -.. py:module:: PowerPlatform.Dataverse.common.constants - -.. autoapi-nested-parse:: - - Constants for Dataverse Web API metadata types. - - These constants define the OData type identifiers used in Web API payloads - for metadata operations. - - - -Attributes ----------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.common.constants.ODATA_TYPE_LOCALIZED_LABEL - PowerPlatform.Dataverse.common.constants.ODATA_TYPE_LABEL - PowerPlatform.Dataverse.common.constants.ODATA_TYPE_LOOKUP_ATTRIBUTE - PowerPlatform.Dataverse.common.constants.ODATA_TYPE_ONE_TO_MANY_RELATIONSHIP - PowerPlatform.Dataverse.common.constants.ODATA_TYPE_MANY_TO_MANY_RELATIONSHIP - PowerPlatform.Dataverse.common.constants.CASCADE_BEHAVIOR_CASCADE - PowerPlatform.Dataverse.common.constants.CASCADE_BEHAVIOR_NO_CASCADE - PowerPlatform.Dataverse.common.constants.CASCADE_BEHAVIOR_REMOVE_LINK - PowerPlatform.Dataverse.common.constants.CASCADE_BEHAVIOR_RESTRICT - - -Module Contents ---------------- - -.. py:data:: ODATA_TYPE_LOCALIZED_LABEL - :value: 'Microsoft.Dynamics.CRM.LocalizedLabel' - - -.. py:data:: ODATA_TYPE_LABEL - :value: 'Microsoft.Dynamics.CRM.Label' - - -.. py:data:: ODATA_TYPE_LOOKUP_ATTRIBUTE - :value: 'Microsoft.Dynamics.CRM.LookupAttributeMetadata' - - -.. py:data:: ODATA_TYPE_ONE_TO_MANY_RELATIONSHIP - :value: 'Microsoft.Dynamics.CRM.OneToManyRelationshipMetadata' - - -.. py:data:: ODATA_TYPE_MANY_TO_MANY_RELATIONSHIP - :value: 'Microsoft.Dynamics.CRM.ManyToManyRelationshipMetadata' - - -.. py:data:: CASCADE_BEHAVIOR_CASCADE - :value: 'Cascade' - - - Perform the action on all referencing table records associated with the referenced table record. - -.. py:data:: CASCADE_BEHAVIOR_NO_CASCADE - :value: 'NoCascade' - - - Do not apply the action to any referencing table records associated with the referenced table record. - -.. py:data:: CASCADE_BEHAVIOR_REMOVE_LINK - :value: 'RemoveLink' - - - Remove the value of the referencing column for all referencing table records when the referenced record is deleted. - -.. py:data:: CASCADE_BEHAVIOR_RESTRICT - :value: 'Restrict' - - - Prevent the referenced table record from being deleted when referencing table records exist. - diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/common/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/common/index.rst deleted file mode 100644 index 55ae1b6f..00000000 --- a/docs_local/autoapi/PowerPlatform/Dataverse/common/index.rst +++ /dev/null @@ -1,22 +0,0 @@ -PowerPlatform.Dataverse.common -============================== - -.. py:module:: PowerPlatform.Dataverse.common - -.. autoapi-nested-parse:: - - Common utilities and constants for the Dataverse SDK. - - This module contains shared constants and utilities used across the SDK. - - - -Submodules ----------- - -.. toctree:: - :maxdepth: 1 - - /autoapi/PowerPlatform/Dataverse/common/constants/index - - diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/core/config/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/core/config/index.rst deleted file mode 100644 index ef0f6d1d..00000000 --- a/docs_local/autoapi/PowerPlatform/Dataverse/core/config/index.rst +++ /dev/null @@ -1,117 +0,0 @@ -PowerPlatform.Dataverse.core.config -=================================== - -.. py:module:: PowerPlatform.Dataverse.core.config - -.. autoapi-nested-parse:: - - Dataverse client configuration. - - Provides :class:`~PowerPlatform.Dataverse.core.config.DataverseConfig`, a lightweight - immutable container for locale and (reserved) HTTP tuning options plus the - convenience constructor :meth:`~PowerPlatform.Dataverse.core.config.DataverseConfig.from_env`. - - - -Classes -------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.core.config.OperationContext - PowerPlatform.Dataverse.core.config.DataverseConfig - - -Module Contents ---------------- - -.. py:class:: OperationContext - - Caller-defined context appended to outbound ``User-Agent`` headers. - - The context string is validated to be semicolon-separated ``key=value`` pairs - using only allowed keys (``app``, ``skill``, ``agent``) with values from - closed allowlists. Free-form text, email addresses, PII, and unknown keys - are rejected. - - :param user_agent_context: Attribution string in ``key=value;key=value`` format. - :type user_agent_context: :class:`str` - - :raises ValueError: If the string is empty, contains control characters, - does not match the required ``key=value`` format, or uses unknown - keys/values. - - - .. py:attribute:: user_agent_context - :type: str - - -.. py:class:: DataverseConfig - - Configuration settings for Dataverse client operations. - - :param language_code: LCID (Locale ID) for localized labels and messages. Default is 1033 (English - United States). - :type language_code: :class:`int` - :param http_retries: Optional maximum number of retry attempts for transient HTTP errors. Reserved for future use. - :type http_retries: :class:`int` or None - :param http_backoff: Optional backoff multiplier (in seconds) between retry attempts. Reserved for future use. - :type http_backoff: :class:`float` or None - :param http_timeout: Optional request timeout in seconds. Reserved for future use. - :type http_timeout: :class:`float` or None - :param log_config: Optional local HTTP diagnostics logging configuration. - When provided, all HTTP requests and responses are logged to timestamped - ``.log`` files with automatic redaction of sensitive headers. - :type log_config: ~PowerPlatform.Dataverse.core.log_config.LogConfig or None - :param operation_context: Optional caller-defined context object appended to the - outbound ``User-Agent`` header as a parenthesized comment. Intended for - plugin/tool attribution. - :type operation_context: ~PowerPlatform.Dataverse.core.config.OperationContext or None - - - .. py:attribute:: language_code - :type: int - :value: 1033 - - - - .. py:attribute:: http_retries - :type: Optional[int] - :value: None - - - - .. py:attribute:: http_backoff - :type: Optional[float] - :value: None - - - - .. py:attribute:: http_timeout - :type: Optional[float] - :value: None - - - - .. py:attribute:: log_config - :type: Optional[PowerPlatform.Dataverse.core.log_config.LogConfig] - :value: None - - - - .. py:attribute:: operation_context - :type: Optional[OperationContext] - :value: None - - - - .. py:method:: from_env() -> DataverseConfig - :classmethod: - - - Create a configuration instance with default settings. - - :return: Configuration instance with default values. - :rtype: ~PowerPlatform.Dataverse.core.config.DataverseConfig - - - diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/core/errors/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/core/errors/index.rst deleted file mode 100644 index 7fb487a0..00000000 --- a/docs_local/autoapi/PowerPlatform/Dataverse/core/errors/index.rst +++ /dev/null @@ -1,185 +0,0 @@ -PowerPlatform.Dataverse.core.errors -=================================== - -.. py:module:: PowerPlatform.Dataverse.core.errors - -.. autoapi-nested-parse:: - - Structured Dataverse exception hierarchy. - - This module provides :class:`~PowerPlatform.Dataverse.core.errors.DataverseError` and - specialized :class:`~PowerPlatform.Dataverse.core.errors.ValidationError`, - :class:`~PowerPlatform.Dataverse.core.errors.MetadataError`, - :class:`~PowerPlatform.Dataverse.core.errors.SQLParseError`, and - :class:`~PowerPlatform.Dataverse.core.errors.HttpError` for validation, metadata, - SQL parsing, and Web API HTTP failures. - - - -Exceptions ----------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.core.errors.DataverseError - PowerPlatform.Dataverse.core.errors.ValidationError - PowerPlatform.Dataverse.core.errors.MetadataError - PowerPlatform.Dataverse.core.errors.SQLParseError - PowerPlatform.Dataverse.core.errors.HttpError - - -Module Contents ---------------- - -.. py:exception:: DataverseError(message: str, code: str, subcode: Optional[str] = None, status_code: Optional[int] = None, details: Optional[Dict[str, Any]] = None, source: Optional[str] = None, is_transient: bool = False) - - Bases: :py:obj:`Exception` - - - Base structured exception for the Dataverse SDK. - - :param message: Human-readable error message. - :type message: :class:`str` - :param code: Error category code (e.g. ``"validation_error"``, ``"http_error"``). - :type code: :class:`str` - :param subcode: Optional subcategory or specific error identifier. - :type subcode: :class:`str` | None - :param status_code: Optional HTTP status code if the error originated from an HTTP response. - :type status_code: :class:`int` | None - :param details: Optional dictionary containing additional diagnostic information. - :type details: :class:`dict` | None - :param source: Error source, either ``"client"`` or ``"server"``. - :type source: :class:`str` - :param is_transient: Whether the error is potentially transient and may succeed on retry. - :type is_transient: :class:`bool` - - Initialize self. See help(type(self)) for accurate signature. - - - .. py:attribute:: message - - - .. py:attribute:: code - - - .. py:attribute:: subcode - :value: None - - - - .. py:attribute:: status_code - :value: None - - - - .. py:attribute:: details - - - .. py:attribute:: source - :value: 'client' - - - - .. py:attribute:: is_transient - :value: False - - - - .. py:attribute:: timestamp - - - .. py:method:: to_dict() -> Dict[str, Any] - - Convert the error to a dictionary representation. - - :return: Dictionary containing all error properties. - :rtype: :class:`dict` - - - -.. py:exception:: ValidationError(message: str, *, subcode: Optional[str] = None, details: Optional[Dict[str, Any]] = None) - - Bases: :py:obj:`DataverseError` - - - Exception raised for client-side validation failures. - - :param message: Human-readable validation error message. - :type message: :class:`str` - :param subcode: Optional specific validation error identifier. - :type subcode: :class:`str` | None - :param details: Optional dictionary with additional validation context. - :type details: :class:`dict` | None - - Initialize self. See help(type(self)) for accurate signature. - - -.. py:exception:: MetadataError(message: str, *, subcode: Optional[str] = None, details: Optional[Dict[str, Any]] = None) - - Bases: :py:obj:`DataverseError` - - - Exception raised for metadata operation failures. - - :param message: Human-readable metadata error message. - :type message: :class:`str` - :param subcode: Optional specific metadata error identifier. - :type subcode: :class:`str` | None - :param details: Optional dictionary with additional metadata context. - :type details: :class:`dict` | None - - Initialize self. See help(type(self)) for accurate signature. - - -.. py:exception:: SQLParseError(message: str, *, subcode: Optional[str] = None, details: Optional[Dict[str, Any]] = None) - - Bases: :py:obj:`DataverseError` - - - Exception raised for SQL query parsing failures. - - :param message: Human-readable SQL parsing error message. - :type message: :class:`str` - :param subcode: Optional specific SQL parsing error identifier. - :type subcode: :class:`str` | None - :param details: Optional dictionary with SQL query context and parse information. - :type details: :class:`dict` | None - - Initialize self. See help(type(self)) for accurate signature. - - -.. py:exception:: HttpError(message: str, status_code: int, is_transient: bool = False, subcode: Optional[str] = None, service_error_code: Optional[str] = None, correlation_id: Optional[str] = None, client_request_id: Optional[str] = None, service_request_id: Optional[str] = None, traceparent: Optional[str] = None, body_excerpt: Optional[str] = None, retry_after: Optional[int] = None, details: Optional[Dict[str, Any]] = None) - - Bases: :py:obj:`DataverseError` - - - Exception raised for HTTP request failures from the Dataverse Web API. - - :param message: Human-readable HTTP error message, typically from the API error response. - :type message: :class:`str` - :param status_code: HTTP status code (e.g. 400, 404, 500). - :type status_code: :class:`int` - :param is_transient: Whether the error is transient (429, 503, 504) and may succeed on retry. - :type is_transient: :class:`bool` - :param subcode: Optional HTTP status category (e.g. ``"4xx"``, ``"5xx"``). - :type subcode: :class:`str` | None - :param service_error_code: Optional Dataverse-specific error code from the API response. - :type service_error_code: :class:`str` | None - :param correlation_id: Optional client-generated correlation ID for tracking requests within an SDK call. - :type correlation_id: :class:`str` | None - :param client_request_id: Optional client-generated request ID injected into outbound headers. - :type client_request_id: :class:`str` | None - :param service_request_id: Optional ``x-ms-service-request-id`` value returned by Dataverse servers. - :type service_request_id: :class:`str` | None - :param traceparent: Optional W3C trace context for distributed tracing. - :type traceparent: :class:`str` | None - :param body_excerpt: Optional excerpt of the response body for diagnostics. - :type body_excerpt: :class:`str` | None - :param retry_after: Optional number of seconds to wait before retrying (from Retry-After header). - :type retry_after: :class:`int` | None - :param details: Optional additional diagnostic details. - :type details: :class:`dict` | None - - Initialize self. See help(type(self)) for accurate signature. - - diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/core/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/core/index.rst deleted file mode 100644 index 31a27202..00000000 --- a/docs_local/autoapi/PowerPlatform/Dataverse/core/index.rst +++ /dev/null @@ -1,25 +0,0 @@ -PowerPlatform.Dataverse.core -============================ - -.. py:module:: PowerPlatform.Dataverse.core - -.. autoapi-nested-parse:: - - Core infrastructure components for the Dataverse SDK. - - This module contains the foundational components including authentication, - configuration, HTTP client, and error handling. - - - -Submodules ----------- - -.. toctree:: - :maxdepth: 1 - - /autoapi/PowerPlatform/Dataverse/core/config/index - /autoapi/PowerPlatform/Dataverse/core/errors/index - /autoapi/PowerPlatform/Dataverse/core/log_config/index - - diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/core/log_config/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/core/log_config/index.rst deleted file mode 100644 index aa4a5e09..00000000 --- a/docs_local/autoapi/PowerPlatform/Dataverse/core/log_config/index.rst +++ /dev/null @@ -1,90 +0,0 @@ -PowerPlatform.Dataverse.core.log_config -======================================= - -.. py:module:: PowerPlatform.Dataverse.core.log_config - -.. autoapi-nested-parse:: - - Local file logging configuration for Dataverse SDK HTTP diagnostics. - - Provides :class:`~PowerPlatform.Dataverse.core.log_config.LogConfig`, an opt-in configuration for writing request/response - traces to ``.log`` files with automatic header redaction and timestamped filenames. - - - -Classes -------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.core.log_config.LogConfig - - -Module Contents ---------------- - -.. py:class:: LogConfig - - Configuration for local HTTP diagnostics logging. - - When provided to :class:`~PowerPlatform.Dataverse.client.DataverseClient` via - :class:`~PowerPlatform.Dataverse.core.config.DataverseConfig`, every HTTP request - and response is logged to timestamped ``.log`` files in the specified folder. - Sensitive headers (e.g. ``Authorization``) are automatically redacted. - - :param log_folder: Directory path for log files. Created automatically if missing. - Default: ``"./dataverse_logs"`` - :param log_file_prefix: Filename prefix. Timestamp is appended automatically. - Default: ``"dataverse"`` → ``dataverse_20260310_143022.log`` - :param max_body_bytes: Maximum bytes of request/response body to capture. - ``0`` (default) disables body capture. Enable only for active debugging - sessions — bodies may contain PII and sensitive business data. - :param redacted_headers: Header names (case-insensitive) whose values are - replaced with ``"[REDACTED]"`` in logs. Defaults include - ``Authorization``, ``Proxy-Authorization``, etc. - :param log_level: Python logging level name. Default: ``"DEBUG"``. - :param max_file_bytes: Max size per log file before rotation (bytes). - Default: ``10_485_760`` (10 MB). - :param backup_count: Number of rotated backup files to keep. Default: ``5``. - - - .. py:attribute:: log_folder - :type: str - :value: './dataverse_logs' - - - - .. py:attribute:: log_file_prefix - :type: str - :value: 'dataverse' - - - - .. py:attribute:: max_body_bytes - :type: int - :value: 0 - - - - .. py:attribute:: redacted_headers - :type: FrozenSet[str] - - - .. py:attribute:: log_level - :type: str - :value: 'DEBUG' - - - - .. py:attribute:: max_file_bytes - :type: int - :value: 10485760 - - - - .. py:attribute:: backup_count - :type: int - :value: 5 - - - diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/data/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/data/index.rst deleted file mode 100644 index 62e70f16..00000000 --- a/docs_local/autoapi/PowerPlatform/Dataverse/data/index.rst +++ /dev/null @@ -1,14 +0,0 @@ -PowerPlatform.Dataverse.data -============================ - -.. py:module:: PowerPlatform.Dataverse.data - -.. autoapi-nested-parse:: - - Data access layer for the Dataverse SDK. - - This module contains OData protocol handling, CRUD operations, metadata management, - SQL query functionality, and file upload capabilities. - - - diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/extensions/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/extensions/index.rst deleted file mode 100644 index 4f88c87c..00000000 --- a/docs_local/autoapi/PowerPlatform/Dataverse/extensions/index.rst +++ /dev/null @@ -1,11 +0,0 @@ -PowerPlatform.Dataverse.extensions -================================== - -.. py:module:: PowerPlatform.Dataverse.extensions - -.. autoapi-nested-parse:: - - Optional extensions for the Dataverse SDK. Currently a placeholder. - - - diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/index.rst deleted file mode 100644 index 8f38b9f8..00000000 --- a/docs_local/autoapi/PowerPlatform/Dataverse/index.rst +++ /dev/null @@ -1,24 +0,0 @@ -PowerPlatform.Dataverse -======================= - -.. py:module:: PowerPlatform.Dataverse - - -Submodules ----------- - -.. toctree:: - :maxdepth: 1 - - /autoapi/PowerPlatform/Dataverse/claude_skill/index - /autoapi/PowerPlatform/Dataverse/client/index - /autoapi/PowerPlatform/Dataverse/common/index - /autoapi/PowerPlatform/Dataverse/core/index - /autoapi/PowerPlatform/Dataverse/data/index - /autoapi/PowerPlatform/Dataverse/extensions/index - /autoapi/PowerPlatform/Dataverse/migration/index - /autoapi/PowerPlatform/Dataverse/models/index - /autoapi/PowerPlatform/Dataverse/operations/index - /autoapi/PowerPlatform/Dataverse/utils/index - - diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/migration/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/migration/index.rst deleted file mode 100644 index 15bc1e31..00000000 --- a/docs_local/autoapi/PowerPlatform/Dataverse/migration/index.rst +++ /dev/null @@ -1,15 +0,0 @@ -PowerPlatform.Dataverse.migration -================================= - -.. py:module:: PowerPlatform.Dataverse.migration - - -Submodules ----------- - -.. toctree:: - :maxdepth: 1 - - /autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index - - diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index.rst deleted file mode 100644 index 0e6e73c2..00000000 --- a/docs_local/autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index.rst +++ /dev/null @@ -1,119 +0,0 @@ -PowerPlatform.Dataverse.migration.migrate_v0_to_v1 -================================================== - -.. py:module:: PowerPlatform.Dataverse.migration.migrate_v0_to_v1 - -.. autoapi-nested-parse:: - - DV-Python-SDK v0 -> v1 GA migration codemod. - - Mechanically rewrites beta (0.1.0b*) call sites to their GA (1.0) equivalents - using LibCST (concrete syntax tree — preserves all whitespace and comments). - - Usage:: - - pip install PowerPlatform-Dataverse-Client[migration] - dataverse-migrate path/to/your/scripts/ - dataverse-migrate path/to/your/scripts/ --dry-run # preview without writing - dataverse-migrate path/to/your/scripts/ --client-var=svc # if client is named 'svc' - - # Or via module for development installs: - python -m PowerPlatform.Dataverse.migration.migrate_v0_to_v1 path/to/your/scripts/ - - Transformations applied - ----------------------- - Builder methods (.filter_* -> .where(col(...)...)):: - - .filter_eq("col", v) -> .where(col("col") == v) - .filter_ne("col", v) -> .where(col("col") != v) - .filter_gt("col", v) -> .where(col("col") > v) - .filter_ge("col", v) -> .where(col("col") >= v) - .filter_lt("col", v) -> .where(col("col") < v) - .filter_le("col", v) -> .where(col("col") <= v) - .filter_contains("col", v) -> .where(col("col").contains(v)) - .filter_startswith("col", v) -> .where(col("col").startswith(v)) - .filter_endswith("col", v) -> .where(col("col").endswith(v)) - .filter_in("col", vals) -> .where(col("col").in_(vals)) - .filter_not_in("col", vals) -> .where(col("col").not_in(vals)) - .filter_null("col") -> .where(col("col").is_null()) - .filter_not_null("col") -> .where(col("col").is_not_null()) - .filter_between("col", lo, hi) -> .where(col("col").between(lo, hi)) - .filter_not_between("col", lo, hi) -> .where(col("col").not_between(lo, hi)) - .filter_raw("expr") -> .where(raw("expr")) - .filter("expr") -> .where(raw("expr")) - .execute(by_page=True) -> .execute_pages() - .execute(by_page=False) -> .execute() (flag removed) - .to_dataframe() -> .execute().to_dataframe() - Inserts .execute() when the receiver is a recognised QueryBuilder chain - (contains .builder(), .select(), .where(), or a .filter_*() call). - - Record namespace:: - - batch.records.get(t, id) -> batch.records.retrieve(t, id) - - Top-level shortcuts (removed at GA):: - - client.create(t, d) -> client.records.create(t, d) - client.update(t, id, d) -> client.records.update(t, id, d) - client.delete(t, id) -> client.records.delete(t, id) - client.get(t, id) -> client.records.get(t, id) [deprecated; see manual section] - client.query_sql(sql) -> client.query.sql(sql) - client.get_table_info(t) -> client.tables.get(t) - client.create_table(t, …) -> client.tables.create(t, …) - client.delete_table(t) -> client.tables.delete(t) - client.list_tables() -> client.tables.list() - client.create_columns(t, …) -> client.tables.add_columns(t, …) - client.delete_columns(t, …) -> client.tables.remove_columns(t, …) - client.upload_file(…) -> client.files.upload(…) - - Import management: - Adds ``from PowerPlatform.Dataverse.models.filters import col`` when a - .filter_* method is rewritten (if col is not already imported). - Adds ``raw`` to the same import when .filter_raw or .filter is rewritten. - - NOT handled by this codemod (manual migration required): - execute(by_page=variable) -> manual review required (variable argument, not literal) - client.records.get(t, id) -> client.records.retrieve(t, id) - Return type changes: beta returns Record (raises on 404); GA retrieve() returns - Record | None. Callers that do not guard against None will fail silently. - client.records.get(t, kw=…) -> client.records.list(t, kw=…) - Return type changes: beta returns Iterable[List[Record]] (pages); GA list() - returns QueryResult (flat iterable over Records). Any ``for page in result: - for rec in page:`` iteration pattern breaks after a mechanical rename. - client.dataframe.get() -> client.query.builder(…).execute().to_dataframe() - Expression reconstruction requires understanding caller intent. - client.query.sql_select()/sql_join()/sql_joins() -> removed (no mechanical replacement) - - - -Functions ---------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.migration.migrate_v0_to_v1.find_manual_patterns - PowerPlatform.Dataverse.migration.migrate_v0_to_v1.migrate_source - PowerPlatform.Dataverse.migration.migrate_v0_to_v1.migrate_file - PowerPlatform.Dataverse.migration.migrate_v0_to_v1.main - - -Module Contents ---------------- - -.. py:function:: find_manual_patterns(source: str, *, client_var: str = 'client') -> List[str] - - Return descriptions of patterns in *source* that require manual migration. - - -.. py:function:: migrate_source(source: str, *, client_var: str = 'client') -> str - - Parse *source*, apply transformations, return migrated source. - - -.. py:function:: migrate_file(path: pathlib.Path, *, dry_run: bool = False, client_var: str = 'client') -> Tuple[bool, List[str]] - - Migrate *path* in place. Returns (was_changed, manual_review_notes). - - -.. py:function:: main(argv: Optional[List[str]] = None) -> int - diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/models/batch/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/models/batch/index.rst deleted file mode 100644 index a0d3d118..00000000 --- a/docs_local/autoapi/PowerPlatform/Dataverse/models/batch/index.rst +++ /dev/null @@ -1,154 +0,0 @@ -PowerPlatform.Dataverse.models.batch -==================================== - -.. py:module:: PowerPlatform.Dataverse.models.batch - -.. autoapi-nested-parse:: - - Public result types for batch operations. - - - -Classes -------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.models.batch.BatchItemResponse - PowerPlatform.Dataverse.models.batch.BatchResult - - -Module Contents ---------------- - -.. py:class:: BatchItemResponse - - Response from a single operation within a batch request. - - Responses are returned in submission order. For operations added to a - changeset, responses appear in the changeset's position in that order. - - :param status_code: HTTP status code for this operation (e.g. 204, 200, 400). - :param content_id: ``Content-ID`` value from the changeset response part, if any. - :param entity_id: GUID extracted from the ``OData-EntityId`` response header. - Set for successful create (POST) operations. - :param data: Parsed JSON response body (e.g. for GET operations). - :param error_message: Error message when the operation failed. - :param error_code: Service error code when the operation failed. - - Example:: - - for item in result.responses: - if item.is_success: - print(f"[OK] {item.status_code} entity_id={item.entity_id}") - else: - print(f"[ERR] {item.status_code}: {item.error_message}") - - - .. py:attribute:: status_code - :type: int - - - .. py:attribute:: content_id - :type: Optional[str] - :value: None - - - - .. py:attribute:: entity_id - :type: Optional[str] - :value: None - - - - .. py:attribute:: data - :type: Optional[Dict[str, Any]] - :value: None - - - - .. py:attribute:: error_message - :type: Optional[str] - :value: None - - - - .. py:attribute:: error_code - :type: Optional[str] - :value: None - - - - .. py:property:: is_success - :type: bool - - - Return True when status_code is 2xx. - - -.. py:class:: BatchResult - - Result of executing a batch request. - - Contains one :class:`BatchItemResponse` per HTTP operation submitted. - Operations that expand to multiple HTTP requests (e.g. ``add_columns`` - with three columns) contribute three entries. - - :param responses: All responses in submission order. - - Example:: - - result = client.batch.new().execute() - print(f"Succeeded: {len(result.succeeded)}, Failed: {len(result.failed)}") - for guid in result.entity_ids: - print(f"[OK] entity_id: {guid}") - - - .. py:attribute:: responses - :type: List[BatchItemResponse] - :value: [] - - - - .. py:property:: succeeded - :type: List[BatchItemResponse] - - - Responses with 2xx status codes. - - - .. py:property:: failed - :type: List[BatchItemResponse] - - - Responses with non-2xx status codes. - - - .. py:property:: has_errors - :type: bool - - - True when any response has a non-2xx status code. - - - .. py:property:: entity_ids - :type: List[str] - - - GUIDs extracted from ``OData-EntityId`` headers of successful responses. - - Returns entity IDs from any successful (2xx) response that includes an - ``OData-EntityId`` header. Both individual ``POST`` (create) and - ``PATCH`` (update) operations return this header with the record's GUID. - ``GET`` and ``DELETE`` operations do not. - - .. note:: - ``CreateMultiple`` and ``UpsertMultiple`` action responses do **not** - return per-record ``OData-EntityId`` headers. Their IDs are in the - JSON response body (``data["Ids"]``). Access them via:: - - for resp in result.succeeded: - if resp.data and "Ids" in resp.data: - bulk_ids = resp.data["Ids"] - - diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index.rst deleted file mode 100644 index 1632cc4a..00000000 --- a/docs_local/autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index.rst +++ /dev/null @@ -1,76 +0,0 @@ -PowerPlatform.Dataverse.models.fetchxml_query -============================================= - -.. py:module:: PowerPlatform.Dataverse.models.fetchxml_query - -.. autoapi-nested-parse:: - - FetchXmlQuery — inert query object returned by QueryOperations.fetchxml(). - - - -Classes -------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.models.fetchxml_query.FetchXmlQuery - - -Module Contents ---------------- - -.. py:class:: FetchXmlQuery(xml: str, entity_name: str, client: PowerPlatform.Dataverse.client.DataverseClient) - - Inert FetchXML query object. No HTTP request is made until - :meth:`execute` or :meth:`execute_pages` is called. - - Obtained via ``client.query.fetchxml(xml)``. - - :param xml: Stripped, well-formed FetchXML string. - :param entity_name: Entity schema name from the ```` element. - :param client: Parent :class:`~PowerPlatform.Dataverse.client.DataverseClient`. - - - .. py:method:: execute() -> PowerPlatform.Dataverse.models.record.QueryResult - - Execute the FetchXML query and return all results as a :class:`~PowerPlatform.Dataverse.models.record.QueryResult`. - - Blocking — fetches all pages upfront and holds every record in memory before - returning. Simple for small-to-medium result sets; use :meth:`execute_pages` - when the result set may be large or you want to process records as they arrive. - - :return: All matching records across all pages. - :rtype: :class:`~PowerPlatform.Dataverse.models.record.QueryResult` - - Example:: - - rows = client.query.fetchxml(xml).execute() - df = rows.to_dataframe() - - - - .. py:method:: execute_pages() -> Iterator[PowerPlatform.Dataverse.models.record.QueryResult] - - Lazily yield one :class:`~PowerPlatform.Dataverse.models.record.QueryResult` per HTTP page. - - Streaming — each iteration fires one HTTP request and yields one page. - Prefer over :meth:`execute` when: - - - The result set may be large and you do not want all records in memory at once. - - You want early exit: stop iterating once you find what you need and the - remaining HTTP round-trips are skipped automatically. - - You need per-page progress reporting or batched downstream writes. - - One-shot — do not iterate more than once. - - :return: Iterator of per-page :class:`~PowerPlatform.Dataverse.models.record.QueryResult` objects. - :rtype: Iterator[:class:`~PowerPlatform.Dataverse.models.record.QueryResult`] - - Example:: - - for page in client.query.fetchxml(xml).execute_pages(): - process(page.to_dataframe()) - - - diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/models/filters/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/models/filters/index.rst deleted file mode 100644 index 1e85b977..00000000 --- a/docs_local/autoapi/PowerPlatform/Dataverse/models/filters/index.rst +++ /dev/null @@ -1,365 +0,0 @@ -PowerPlatform.Dataverse.models.filters -====================================== - -.. py:module:: PowerPlatform.Dataverse.models.filters - -.. autoapi-nested-parse:: - - Composable OData filter expressions for the Dataverse SDK. - - Provides an expression tree that compiles to OData ``$filter`` strings, - with Python operator overloads (``&``, ``|``, ``~``) for composing - complex filter conditions. - - Example:: - - from PowerPlatform.Dataverse.models.filters import col, raw - - # Preferred GA idiom — col() proxy - expr = col("statecode") == 0 - print(expr.to_odata()) # statecode eq 0 - - # Complex composition with OR and AND - expr = (col("statecode") == 0) | (col("statecode") == 1) & (col("revenue") > 100000) - print(expr.to_odata()) - - # In / not-in - expr = col("statecode").in_([0, 1, 2]) - print(expr.to_odata()) - # Microsoft.Dynamics.CRM.In(PropertyName='statecode',PropertyValues=["0","1","2"]) - - # Raw OData escape hatch (no deprecation warning) - expr = raw("Microsoft.Dynamics.CRM.Today(PropertyName='createdon')") - - # Negation - expr = ~(col("statecode") == 1) - print(expr.to_odata()) # not (statecode eq 1) - - - -Classes -------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.models.filters.FilterExpression - PowerPlatform.Dataverse.models.filters.ColumnProxy - - -Functions ---------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.models.filters.col - PowerPlatform.Dataverse.models.filters.raw - PowerPlatform.Dataverse.models.filters.eq - PowerPlatform.Dataverse.models.filters.ne - PowerPlatform.Dataverse.models.filters.gt - PowerPlatform.Dataverse.models.filters.ge - PowerPlatform.Dataverse.models.filters.lt - PowerPlatform.Dataverse.models.filters.le - PowerPlatform.Dataverse.models.filters.contains - PowerPlatform.Dataverse.models.filters.startswith - PowerPlatform.Dataverse.models.filters.endswith - PowerPlatform.Dataverse.models.filters.between - PowerPlatform.Dataverse.models.filters.is_null - PowerPlatform.Dataverse.models.filters.is_not_null - PowerPlatform.Dataverse.models.filters.filter_in - PowerPlatform.Dataverse.models.filters.not_in - PowerPlatform.Dataverse.models.filters.not_between - - -Module Contents ---------------- - -.. py:class:: FilterExpression - - Base class for composable OData filter expressions. - - Supports Python operator overloads for logical composition: - - - ``expr1 & expr2`` produces ``(expr1 and expr2)`` - - ``expr1 | expr2`` produces ``(expr1 or expr2)`` - - ``~expr`` produces ``not (expr)`` - - - .. py:method:: to_odata() -> str - :abstractmethod: - - - Compile this expression to an OData ``$filter`` string. - - - -.. py:class:: ColumnProxy(name: str) - - Fluent proxy for building OData filter expressions from a column name. - - Returned by :func:`col`. Operator overloads and methods produce - :class:`FilterExpression` instances that can be passed to - ``QueryBuilder.where()``. - - Example:: - - from PowerPlatform.Dataverse.models.filters import col - - expr = col("statecode") == 0 # equality - expr = col("revenue") > 1_000_000 # comparison - expr = col("name").like("Contoso%") # startswith - expr = col("name").is_null() # null check - expr = col("statecode").in_([0, 1]) # in - - - .. py:method:: is_null() -> FilterExpression - - Column equals null: ``column eq null``. - - - - .. py:method:: is_not_null() -> FilterExpression - - Column not null: ``column ne null``. - - - - .. py:method:: in_(values: Collection[Any]) -> FilterExpression - - In filter using ``Microsoft.Dynamics.CRM.In``. - - :param values: Non-empty collection of values. - :raises ValueError: If ``values`` is empty. - - - - .. py:method:: not_in(values: Collection[Any]) -> FilterExpression - - Not-in filter using ``Microsoft.Dynamics.CRM.NotIn``. - - :param values: Non-empty collection of values. - :raises ValueError: If ``values`` is empty. - - - - .. py:method:: between(lo: Any, hi: Any) -> FilterExpression - - Between filter: ``(column ge lo and column le hi)``. - - - - .. py:method:: not_between(lo: Any, hi: Any) -> FilterExpression - - Not-between filter: ``not (column ge lo and column le hi)``. - - - - .. py:method:: contains(value: str) -> FilterExpression - - Contains filter: ``contains(column, value)``. - - - - .. py:method:: startswith(value: str) -> FilterExpression - - Startswith filter: ``startswith(column, value)``. - - - - .. py:method:: endswith(value: str) -> FilterExpression - - Endswith filter: ``endswith(column, value)``. - - - - .. py:method:: like(pattern: str) -> FilterExpression - - Pattern-match filter compiled to the closest OData equivalent. - - +-----------------+-----------------------------+-------------------------------------+ - | Pattern form | Example | Compiles to | - +=================+=============================+=====================================+ - | ``val%`` | ``like("Contoso%")`` | ``startswith(column,'Contoso')`` | - +-----------------+-----------------------------+-------------------------------------+ - | ``%val`` | ``like("%Ltd")`` | ``endswith(column,'Ltd')`` | - +-----------------+-----------------------------+-------------------------------------+ - | ``%val%`` | ``like("%Corp%")`` | ``contains(column,'Corp')`` | - +-----------------+-----------------------------+-------------------------------------+ - | No wildcard | ``like("Contoso")`` | ``column eq 'Contoso'`` | - +-----------------+-----------------------------+-------------------------------------+ - | Other | ``like("Con%oso")`` | :class:`ValueError` | - +-----------------+-----------------------------+-------------------------------------+ - - :param pattern: LIKE-style pattern string. - :raises ValueError: If the pattern cannot be reduced to a single OData function. - - - - .. py:method:: not_like(pattern: str) -> FilterExpression - - Negated pattern-match filter; mirrors :meth:`like` rules then negates. - - :param pattern: LIKE-style pattern string (same rules as :meth:`like`). - :raises ValueError: If the pattern cannot be reduced to a single OData function. - - - -.. py:function:: col(name: str) -> ColumnProxy - - Return a :class:`ColumnProxy` for building filter expressions. - - This is the preferred GA idiom for constructing filter expressions:: - - from PowerPlatform.Dataverse.models.filters import col - - expr = col("statecode") == 0 - expr = col("revenue") > 1_000_000 - expr = col("name").like("Contoso%") - expr = col("statecode").in_([0, 1]) - expr = col("parentaccountid").is_null() - - :param name: Column logical name (case-insensitive, will be lowercased). - :return: A :class:`ColumnProxy` bound to the column. - :raises ValueError: If ``name`` is empty. - - -.. py:function:: raw(filter_string: str) -> FilterExpression - - Verbatim OData filter expression (passed through unchanged). - - This function is **not** deprecated — it is the OData escape hatch with - no typed replacement. - - :param filter_string: Raw OData filter string. - :return: A :class:`FilterExpression`. - - Example:: - - raw("Microsoft.Dynamics.CRM.Today(PropertyName='createdon')") - - -.. py:function:: eq(column: str, value: Any) -> FilterExpression - - Equality filter: ``column eq value``. - - .. deprecated:: - Use ``col(column) == value`` instead. - - -.. py:function:: ne(column: str, value: Any) -> FilterExpression - - Not-equal filter: ``column ne value``. - - .. deprecated:: - Use ``col(column) != value`` instead. - - -.. py:function:: gt(column: str, value: Any) -> FilterExpression - - Greater-than filter: ``column gt value``. - - .. deprecated:: - Use ``col(column) > value`` instead. - - -.. py:function:: ge(column: str, value: Any) -> FilterExpression - - Greater-than-or-equal filter: ``column ge value``. - - .. deprecated:: - Use ``col(column) >= value`` instead. - - -.. py:function:: lt(column: str, value: Any) -> FilterExpression - - Less-than filter: ``column lt value``. - - .. deprecated:: - Use ``col(column) < value`` instead. - - -.. py:function:: le(column: str, value: Any) -> FilterExpression - - Less-than-or-equal filter: ``column le value``. - - .. deprecated:: - Use ``col(column) <= value`` instead. - - -.. py:function:: contains(column: str, value: str) -> FilterExpression - - Contains filter: ``contains(column, value)``. - - .. deprecated:: - Use ``col(column).contains(value)`` instead. - - -.. py:function:: startswith(column: str, value: str) -> FilterExpression - - Startswith filter: ``startswith(column, value)``. - - .. deprecated:: - Use ``col(column).startswith(value)`` instead. - - -.. py:function:: endswith(column: str, value: str) -> FilterExpression - - Endswith filter: ``endswith(column, value)``. - - .. deprecated:: - Use ``col(column).endswith(value)`` instead. - - -.. py:function:: between(column: str, low: Any, high: Any) -> FilterExpression - - Between filter: ``(column ge low and column le high)``. - - .. deprecated:: - Use ``col(column).between(low, high)`` instead. - - -.. py:function:: is_null(column: str) -> FilterExpression - - Null check: ``column eq null``. - - .. deprecated:: - Use ``col(column).is_null()`` instead. - - -.. py:function:: is_not_null(column: str) -> FilterExpression - - Not-null check: ``column ne null``. - - .. deprecated:: - Use ``col(column).is_not_null()`` instead. - - -.. py:function:: filter_in(column: str, values: Collection[Any]) -> FilterExpression - - In filter using ``Microsoft.Dynamics.CRM.In``. - - Named ``filter_in`` because ``in`` is a Python keyword. - - .. deprecated:: - Use ``col(column).in_(values)`` instead. - - :raises ValueError: If ``values`` is empty. - - -.. py:function:: not_in(column: str, values: Collection[Any]) -> FilterExpression - - Not-in filter using ``Microsoft.Dynamics.CRM.NotIn``. - - .. deprecated:: - Use ``col(column).not_in(values)`` instead. - - :raises ValueError: If ``values`` is empty. - - -.. py:function:: not_between(column: str, low: Any, high: Any) -> FilterExpression - - Not-between filter: ``not (column ge low and column le high)``. - - .. deprecated:: - Use ``col(column).not_between(low, high)`` instead. - - diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/models/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/models/index.rst deleted file mode 100644 index b7ae9520..00000000 --- a/docs_local/autoapi/PowerPlatform/Dataverse/models/index.rst +++ /dev/null @@ -1,41 +0,0 @@ -PowerPlatform.Dataverse.models -============================== - -.. py:module:: PowerPlatform.Dataverse.models - -.. autoapi-nested-parse:: - - Data models and type definitions for the Dataverse SDK. - - Provides dataclasses and helpers for Dataverse entities: - - - :class:`~PowerPlatform.Dataverse.models.query_builder.QueryBuilder`: Fluent query builder. - - :mod:`~PowerPlatform.Dataverse.models.filters`: Composable OData filter expressions - via :func:`~PowerPlatform.Dataverse.models.filters.col` and - :func:`~PowerPlatform.Dataverse.models.filters.raw`. - - :class:`~PowerPlatform.Dataverse.models.record.QueryResult`: Iterable result wrapper. - - :class:`~PowerPlatform.Dataverse.models.record.Record`: Dataverse entity record. - - :class:`~PowerPlatform.Dataverse.models.upsert.UpsertItem`: Upsert operation item. - - :class:`~PowerPlatform.Dataverse.models.fetchxml_query.FetchXmlQuery`: FetchXML query object. - - :class:`~PowerPlatform.Dataverse.models.protocol.DataverseModel`: Typed-model protocol. - - - -Submodules ----------- - -.. toctree:: - :maxdepth: 1 - - /autoapi/PowerPlatform/Dataverse/models/batch/index - /autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index - /autoapi/PowerPlatform/Dataverse/models/filters/index - /autoapi/PowerPlatform/Dataverse/models/labels/index - /autoapi/PowerPlatform/Dataverse/models/protocol/index - /autoapi/PowerPlatform/Dataverse/models/query_builder/index - /autoapi/PowerPlatform/Dataverse/models/record/index - /autoapi/PowerPlatform/Dataverse/models/relationship/index - /autoapi/PowerPlatform/Dataverse/models/table_info/index - /autoapi/PowerPlatform/Dataverse/models/upsert/index - - diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/models/labels/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/models/labels/index.rst deleted file mode 100644 index f2df2a89..00000000 --- a/docs_local/autoapi/PowerPlatform/Dataverse/models/labels/index.rst +++ /dev/null @@ -1,113 +0,0 @@ -PowerPlatform.Dataverse.models.labels -===================================== - -.. py:module:: PowerPlatform.Dataverse.models.labels - -.. autoapi-nested-parse:: - - Label models for Dataverse metadata. - - - -Classes -------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.models.labels.LocalizedLabel - PowerPlatform.Dataverse.models.labels.Label - - -Module Contents ---------------- - -.. py:class:: LocalizedLabel - - Represents a localized label with a language code. - - :param label: The text of the label. - :type label: str - :param language_code: The language code (LCID), e.g., 1033 for English. - :type language_code: int - :param additional_properties: Optional dict of additional properties to include - in the Web API payload. These are merged last and can override default values. - :type additional_properties: Optional[Dict[str, Any]] - - - .. py:attribute:: label - :type: str - - - .. py:attribute:: language_code - :type: int - - - .. py:attribute:: additional_properties - :type: Optional[Dict[str, Any]] - :value: None - - - - .. py:method:: to_dict() -> Dict[str, Any] - - Convert to Web API JSON format. - - Example:: - - >>> label = LocalizedLabel(label="Account", language_code=1033) - >>> label.to_dict() - { - '@odata.type': 'Microsoft.Dynamics.CRM.LocalizedLabel', - 'Label': 'Account', - 'LanguageCode': 1033 - } - - - -.. py:class:: Label - - Represents a label that can have multiple localized versions. - - :param localized_labels: List of LocalizedLabel instances. - :type localized_labels: List[LocalizedLabel] - :param user_localized_label: Optional user-specific localized label. - :type user_localized_label: Optional[LocalizedLabel] - :param additional_properties: Optional dict of additional properties to include - in the Web API payload. These are merged last and can override default values. - :type additional_properties: Optional[Dict[str, Any]] - - - .. py:attribute:: localized_labels - :type: List[LocalizedLabel] - - - .. py:attribute:: user_localized_label - :type: Optional[LocalizedLabel] - :value: None - - - - .. py:attribute:: additional_properties - :type: Optional[Dict[str, Any]] - :value: None - - - - .. py:method:: to_dict() -> Dict[str, Any] - - Convert to Web API JSON format. - - Example:: - - >>> label = Label(localized_labels=[LocalizedLabel("Account", 1033)]) - >>> label.to_dict() - { - '@odata.type': 'Microsoft.Dynamics.CRM.Label', - 'LocalizedLabels': [ - {'@odata.type': '...', 'Label': 'Account', 'LanguageCode': 1033} - ], - 'UserLocalizedLabel': {'@odata.type': '...', 'Label': 'Account', ...} - } - - - diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/models/protocol/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/models/protocol/index.rst deleted file mode 100644 index 03a4fc46..00000000 --- a/docs_local/autoapi/PowerPlatform/Dataverse/models/protocol/index.rst +++ /dev/null @@ -1,94 +0,0 @@ -PowerPlatform.Dataverse.models.protocol -======================================= - -.. py:module:: PowerPlatform.Dataverse.models.protocol - -.. autoapi-nested-parse:: - - DataverseModel structural Protocol for typed entity integration. - - - -Classes -------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.models.protocol.DataverseModel - - -Module Contents ---------------- - -.. py:class:: DataverseModel - - Bases: :py:obj:`Protocol` - - - Structural Protocol enabling typed entity instances to be passed to - ``records.create()`` and ``records.update()``. - - Implement this Protocol on any entity class (dataclass, Pydantic model, - hand-rolled) to enable it to be passed directly to CRUD operations without - specifying the table name or converting to dict manually. - - Required class variables: - - - ``__entity_logical_name__`` — Dataverse logical entity name (e.g. ``"account"``) - - ``__entity_set_name__`` — OData entity set name (e.g. ``"accounts"``) - - Required instance methods: - - - ``to_dict()`` — return record payload as ``dict`` - - ``from_dict(data)`` — classmethod to reconstruct from a response ``dict`` - - Example:: - - from dataclasses import dataclass - from PowerPlatform.Dataverse import DataverseModel - - @dataclass - class Account: - __entity_logical_name__ = "account" - __entity_set_name__ = "accounts" - name: str = "" - telephone1: str = "" - - def to_dict(self) -> dict: - return {"name": self.name, "telephone1": self.telephone1} - - @classmethod - def from_dict(cls, data: dict) -> "Account": - return cls( - name=data.get("name", ""), - telephone1=data.get("telephone1", ""), - ) - - # isinstance() works today — Protocol is runtime_checkable: - assert isinstance(Account(), DataverseModel) - - # Type your own helpers against the Protocol now: - def save(entity: DataverseModel) -> None: - data = entity.to_dict() - client.records.create(entity.__entity_logical_name__, data) - - .. note:: - - Direct dispatch (``client.records.create(entity)`` without a table name - or dict) is not yet supported and will be added in a future release. - - - .. py:method:: to_dict() -> dict - - Return the record payload as a plain dictionary. - - - - .. py:method:: from_dict(data: dict) -> DataverseModel - :classmethod: - - - Reconstruct an instance from a response dictionary. - - - diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/models/query_builder/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/models/query_builder/index.rst deleted file mode 100644 index 9e8a9583..00000000 --- a/docs_local/autoapi/PowerPlatform/Dataverse/models/query_builder/index.rst +++ /dev/null @@ -1,332 +0,0 @@ -PowerPlatform.Dataverse.models.query_builder -============================================ - -.. py:module:: PowerPlatform.Dataverse.models.query_builder - -.. autoapi-nested-parse:: - - Fluent query builder for constructing OData queries. - - Provides a type-safe, discoverable interface for building complex queries - against Dataverse tables with method chaining. - - Example:: - - # Via client (recommended) -- flat iteration over records - from PowerPlatform.Dataverse.models import col - - for record in (client.query.builder("account") - .select("name", "revenue") - .where(col("statecode") == 0) - .where(col("revenue") > 1_000_000) - .order_by("revenue", descending=True) - .top(100) - .execute()): - print(record["name"]) - - # With composable expression tree - from PowerPlatform.Dataverse.models import col, raw - - for record in (client.query.builder("account") - .select("name", "revenue") - .where((col("statecode") == 0) | (col("statecode") == 1)) - .where(col("revenue") > 100000) - .top(100) - .execute()): - print(record["name"]) - - # Lazy paged iteration (one QueryResult per HTTP page) - for page in (client.query.builder("account") - .select("name") - .execute_pages()): - process_batch(page) - - # Get results as a pandas DataFrame - df = (client.query.builder("account") - .select("name", "telephone1") - .where(col("statecode") == 0) - .top(100) - .execute() - .to_dataframe()) - - - -Classes -------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.models.query_builder.QueryParams - PowerPlatform.Dataverse.models.query_builder.ExpandOption - PowerPlatform.Dataverse.models.query_builder.QueryBuilder - - -Module Contents ---------------- - -.. py:class:: QueryParams - - Bases: :py:obj:`TypedDict` - - - Typed dictionary returned by ``QueryBuilder.build()``. - - Provides IDE autocomplete when passing build results to - ``client.records.list()`` manually. - - Initialize self. See help(type(self)) for accurate signature. - - - .. py:attribute:: table - :type: str - - - .. py:attribute:: select - :type: List[str] - - - .. py:attribute:: filter - :type: str - - - .. py:attribute:: orderby - :type: List[str] - - - .. py:attribute:: expand - :type: List[str] - - - .. py:attribute:: top - :type: int - - - .. py:attribute:: page_size - :type: int - - - .. py:attribute:: count - :type: bool - - - .. py:attribute:: include_annotations - :type: str - - -.. py:class:: ExpandOption(relation: str) - - Structured options for an ``$expand`` navigation property. - - Allows specifying nested ``$select``, ``$filter``, ``$orderby``, and - ``$top`` options for a single navigation property expansion, following - the OData ``$expand`` syntax. - - :param relation: Navigation property name (case-sensitive). - :type relation: str - - Example:: - - # Expand Account_Tasks with nested options - opt = (ExpandOption("Account_Tasks") - .select("subject", "createdon") - .filter("contains(subject,'Task')") - .order_by("createdon", descending=True) - .top(5)) - - query = (client.query.builder("account") - .select("name") - .expand(opt) - .execute()) - - - .. py:attribute:: relation - - - .. py:method:: select(*columns: str) -> ExpandOption - - Select specific columns from the expanded entity. - - :param columns: Column names to select. - :return: Self for method chaining. - - - - .. py:method:: filter(filter_str: str) -> ExpandOption - - Filter the expanded collection. - - :param filter_str: OData ``$filter`` expression. - :return: Self for method chaining. - - - - .. py:method:: order_by(column: str, descending: bool = False) -> ExpandOption - - Sort the expanded collection. - - :param column: Column name to sort by. - :param descending: Sort descending if ``True``. - :return: Self for method chaining. - - - - .. py:method:: top(count: int) -> ExpandOption - - Limit expanded results. - - :param count: Maximum number of expanded records. - :return: Self for method chaining. - - - - .. py:method:: to_odata() -> str - - Compile to OData ``$expand`` syntax. - - :return: OData expand string like ``"Nav($select=col1,col2;$filter=...)"`` - :rtype: str - - - -.. py:class:: QueryBuilder(table: str) - - Bases: :py:obj:`_QueryBuilderBase` - - - Fluent interface for building and executing OData queries against a sync client. - - Provides method chaining for constructing complex queries with - composable filter expressions. Can be used standalone (via ``build()``) - or bound to a client (via :meth:`execute`). - - :param table: Table schema name to query. - :type table: str - :raises ValueError: If ``table`` is empty. - - .. rubric:: Example - - Standalone query construction:: - - from PowerPlatform.Dataverse.models import col - - query = (QueryBuilder("account") - .select("name") - .where(col("statecode") == 0) - .top(10)) - params = query.build() - # {"table": "account", "select": ["name"], - # "filter": "statecode eq 0", "top": 10} - - - .. py:method:: execute(*, by_page=_BY_PAGE_UNSET) -> Union[PowerPlatform.Dataverse.models.record.QueryResult, Iterator[PowerPlatform.Dataverse.models.record.QueryResult]] - - Execute the query and return results. - - Returns a :class:`~PowerPlatform.Dataverse.models.record.QueryResult` - with all pages collected. Use :meth:`execute_pages` for lazy per-page - iteration. - - This method is only available when the QueryBuilder was created - via ``client.query.builder(table)``. Standalone ``QueryBuilder`` - instances should use ``build()`` to get parameters and pass them - to ``client.records.list()`` manually. - - At least one of ``select()``, ``where()``, or ``top()`` must be - called before ``execute()``; otherwise a :class:`ValueError` is - raised to prevent accidental full-table scans. - - .. deprecated:: - The ``by_page`` parameter is deprecated. Use :meth:`execute_pages` - for lazy per-page iteration, or plain ``execute()`` (no flag) for - the default eager result. - - :return: :class:`~PowerPlatform.Dataverse.models.record.QueryResult` - with all pages collected (default), or page iterator (deprecated - ``by_page=True``). - :rtype: QueryResult or Iterator[QueryResult] - :raises ValueError: If no ``select``, ``where``, or ``top`` - constraint has been set. - :raises RuntimeError: If the query was not created via - ``client.query.builder()``. - - Example:: - - from PowerPlatform.Dataverse.models import col - - for record in (client.query.builder("account") - .select("name") - .where(col("statecode") == 0) - .execute()): - print(record["name"]) - - - - .. py:method:: execute_pages() -> Iterator[PowerPlatform.Dataverse.models.record.QueryResult] - - Lazily yield one :class:`~PowerPlatform.Dataverse.models.record.QueryResult` - per HTTP page. - - Each iteration triggers a network request via ``@odata.nextLink``. - One-shot — do not iterate more than once. - - At least one of ``select()``, ``where()``, or ``top()`` must be - called before ``execute_pages()``; otherwise a :class:`ValueError` is - raised to prevent accidental full-table scans. - - :return: Iterator of per-page :class:`~PowerPlatform.Dataverse.models.record.QueryResult`. - :rtype: Iterator[QueryResult] - :raises ValueError: If no ``select``, ``where``, or ``top`` - constraint has been set. - :raises RuntimeError: If the query was not created via - ``client.query.builder()``. - - Example:: - - from PowerPlatform.Dataverse.models import col - - for page in (client.query.builder("account") - .select("name") - .where(col("statecode") == 0) - .execute_pages()): - process(page.to_dataframe()) - - - - .. py:method:: to_dataframe() -> pandas.DataFrame - - Execute the query and return results as a pandas DataFrame. - - .. deprecated:: - Use ``QueryBuilder.execute().to_dataframe()`` instead. - ``QueryBuilder.to_dataframe()`` will be removed in a future release. - - All pages are consolidated into a single DataFrame. - - This method is only available when the QueryBuilder was created - via ``client.query.builder(table)``. - - At least one of ``select()``, ``where()``, or ``top()`` must be - called before ``to_dataframe()``; otherwise a :class:`ValueError` - is raised to prevent accidental full-table scans. - - :return: DataFrame containing all matching records. Returns an empty - DataFrame when no records match. - :rtype: ~pandas.DataFrame - :raises ValueError: If no ``select``, ``where``, or ``top`` - constraint has been set. - :raises RuntimeError: If the query was not created via - ``client.query.builder()``. - - Example:: - - from PowerPlatform.Dataverse.models import col - - df = (client.query.builder("account") - .select("name", "telephone1") - .where(col("statecode") == 0) - .top(100) - .execute() - .to_dataframe()) - - - diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/models/record/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/models/record/index.rst deleted file mode 100644 index d92274ed..00000000 --- a/docs_local/autoapi/PowerPlatform/Dataverse/models/record/index.rst +++ /dev/null @@ -1,152 +0,0 @@ -PowerPlatform.Dataverse.models.record -===================================== - -.. py:module:: PowerPlatform.Dataverse.models.record - -.. autoapi-nested-parse:: - - Record data model for Dataverse entities. - - - -Classes -------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.models.record.Record - PowerPlatform.Dataverse.models.record.QueryResult - - -Module Contents ---------------- - -.. py:class:: Record - - Strongly-typed Dataverse record with dict-like backward compatibility. - - Wraps raw OData response data into a structured object while preserving - ``result["key"]`` access patterns for existing code. - - :param id: Record GUID. Empty string if not extracted (e.g. paginated - results, SQL queries). - :type id: :class:`str` - :param table: Table schema name used in the request. - :type table: :class:`str` - :param data: Record field data as key-value pairs. - :type data: :class:`dict` - :param etag: ETag for optimistic concurrency, extracted from - ``@odata.etag`` in the API response. - :type etag: :class:`str` or None - - Example:: - - record = client.records.get("account", account_id, select=["name"]) - print(record.id) # structured access - print(record["name"]) # dict-like access (backward compat) - - - .. py:attribute:: id - :type: str - :value: '' - - - - .. py:attribute:: table - :type: str - :value: '' - - - - .. py:attribute:: data - :type: Dict[str, Any] - - - .. py:attribute:: etag - :type: Optional[str] - :value: None - - - - .. py:method:: get(key: str, default: Any = None) -> Any - - Return value for *key*, or *default* if not present. - - - - .. py:method:: keys() -> KeysView[str] - - Return data keys. - - - - .. py:method:: values() -> ValuesView[Any] - - Return data values. - - - - .. py:method:: items() -> ItemsView[str, Any] - - Return data items. - - - - .. py:method:: from_api_response(table: str, response_data: Dict[str, Any], *, record_id: str = '') -> Record - :classmethod: - - - Create a :class:`Record` from a raw OData API response. - - Strips ``@odata.*`` annotation keys from the data and extracts the - ``@odata.etag`` value if present. - - :param table: Table schema name. - :type table: :class:`str` - :param response_data: Raw JSON dict from the OData response. - :type response_data: :class:`dict` - :param record_id: Known record GUID. Pass explicitly when available - (e.g. single-record get). Defaults to empty string. - :type record_id: :class:`str` - :rtype: :class:`Record` - - - - .. py:method:: to_dict() -> Dict[str, Any] - - Return a plain dict copy of the record data (excludes metadata). - - - -.. py:class:: QueryResult(records: List[Record]) - - Iterable wrapper around a list of :class:`Record` objects. - - Returned by :meth:`~PowerPlatform.Dataverse.models.query_builder.QueryBuilder.execute` - (flat mode) and :meth:`~PowerPlatform.Dataverse.operations.records.RecordOperations.list`. - - Backward-compatible: ``for r in result`` continues to work without change. - - :param records: Collected records from all pages. - :type records: list[:class:`Record`] - - - .. py:attribute:: records - :type: List[Record] - - - .. py:method:: first() -> Optional[Record] - - Return the first record, or ``None`` if the result is empty. - - - - .. py:method:: to_dataframe() -> Any - - Return all records as a pandas DataFrame. - - :raises ImportError: If pandas is not installed. - :rtype: ~pandas.DataFrame - - - diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/models/relationship/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/models/relationship/index.rst deleted file mode 100644 index 06160b7a..00000000 --- a/docs_local/autoapi/PowerPlatform/Dataverse/models/relationship/index.rst +++ /dev/null @@ -1,471 +0,0 @@ -PowerPlatform.Dataverse.models.relationship -=========================================== - -.. py:module:: PowerPlatform.Dataverse.models.relationship - -.. autoapi-nested-parse:: - - Relationship models for Dataverse (input and output). - - - -Classes -------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.models.relationship.CascadeConfiguration - PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata - PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata - PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata - PowerPlatform.Dataverse.models.relationship.RelationshipInfo - - -Module Contents ---------------- - -.. py:class:: CascadeConfiguration - - Defines cascade behavior for relationship operations. - - :param assign: Cascade behavior for assign operations. - :type assign: str - :param delete: Cascade behavior for delete operations. - :type delete: str - :param merge: Cascade behavior for merge operations. - :type merge: str - :param reparent: Cascade behavior for reparent operations. - :type reparent: str - :param share: Cascade behavior for share operations. - :type share: str - :param unshare: Cascade behavior for unshare operations. - :type unshare: str - :param additional_properties: Optional dict of additional properties to include - in the Web API payload (e.g., "Archive", "RollupView"). These are merged - last and can override default values. - :type additional_properties: Optional[Dict[str, Any]] - - Valid values for each parameter: - - "Cascade": Perform the operation on all related records - - "NoCascade": Do not perform the operation on related records - - "RemoveLink": Remove the relationship link but keep the records - - "Restrict": Prevent the operation if related records exist - - - .. py:attribute:: assign - :type: str - :value: 'NoCascade' - - - - .. py:attribute:: delete - :type: str - :value: 'RemoveLink' - - - - .. py:attribute:: merge - :type: str - :value: 'NoCascade' - - - - .. py:attribute:: reparent - :type: str - :value: 'NoCascade' - - - - .. py:attribute:: share - :type: str - :value: 'NoCascade' - - - - .. py:attribute:: unshare - :type: str - :value: 'NoCascade' - - - - .. py:attribute:: additional_properties - :type: Optional[Dict[str, Any]] - :value: None - - - - .. py:method:: to_dict() -> Dict[str, Any] - - Convert to Web API JSON format. - - Example:: - - >>> config = CascadeConfiguration(delete="Cascade", assign="NoCascade") - >>> config.to_dict() - { - 'Assign': 'NoCascade', - 'Delete': 'Cascade', - 'Merge': 'NoCascade', - 'Reparent': 'NoCascade', - 'Share': 'NoCascade', - 'Unshare': 'NoCascade' - } - - - -.. py:class:: LookupAttributeMetadata - - Metadata for a lookup attribute. - - :param schema_name: Schema name for the attribute (e.g., "new_AccountId"). - :type schema_name: str - :param display_name: Display name for the attribute. - :type display_name: Label - :param description: Optional description of the attribute. - :type description: Optional[Label] - :param required_level: Requirement level for the attribute. - :type required_level: str - :param additional_properties: Optional dict of additional properties to include - in the Web API payload. Useful for setting properties like "Targets" (to - specify which entity types the lookup can reference), "LogicalName", - "IsSecured", "IsValidForAdvancedFind", etc. These are merged last and - can override default values. - :type additional_properties: Optional[Dict[str, Any]] - - Valid required_level values: - - "None": The attribute is optional - - "Recommended": The attribute is recommended - - "ApplicationRequired": The attribute is required - - - .. py:attribute:: schema_name - :type: str - - - .. py:attribute:: display_name - :type: PowerPlatform.Dataverse.models.labels.Label - - - .. py:attribute:: description - :type: Optional[PowerPlatform.Dataverse.models.labels.Label] - :value: None - - - - .. py:attribute:: required_level - :type: str - :value: 'None' - - - - .. py:attribute:: additional_properties - :type: Optional[Dict[str, Any]] - :value: None - - - - .. py:method:: to_dict() -> Dict[str, Any] - - Convert to Web API JSON format. - - Example:: - - >>> lookup = LookupAttributeMetadata( - ... schema_name="new_AccountId", - ... display_name=Label([LocalizedLabel("Account", 1033)]) - ... ) - >>> lookup.to_dict() - { - '@odata.type': 'Microsoft.Dynamics.CRM.LookupAttributeMetadata', - 'SchemaName': 'new_AccountId', - 'AttributeType': 'Lookup', - 'AttributeTypeName': {'Value': 'LookupType'}, - 'DisplayName': {...}, - 'RequiredLevel': {'Value': 'None', 'CanBeChanged': True, ...} - } - - - -.. py:class:: OneToManyRelationshipMetadata - - Metadata for a one-to-many entity relationship. - - :param schema_name: Schema name for the relationship (e.g., "new_Account_Orders"). - :type schema_name: str - :param referenced_entity: Logical name of the referenced (parent) entity. - :type referenced_entity: str - :param referencing_entity: Logical name of the referencing (child) entity. - :type referencing_entity: str - :param referenced_attribute: Attribute on the referenced entity (typically the primary key). - :type referenced_attribute: str - :param cascade_configuration: Cascade behavior configuration. - :type cascade_configuration: CascadeConfiguration - :param referencing_attribute: Optional name for the referencing attribute (usually auto-generated). - :type referencing_attribute: Optional[str] - :param additional_properties: Optional dict of additional properties to include - in the Web API payload. Useful for setting inherited properties like - "IsValidForAdvancedFind", "IsCustomizable", "SecurityTypes", etc. - These are merged last and can override default values. - :type additional_properties: Optional[Dict[str, Any]] - - - .. py:attribute:: schema_name - :type: str - - - .. py:attribute:: referenced_entity - :type: str - - - .. py:attribute:: referencing_entity - :type: str - - - .. py:attribute:: referenced_attribute - :type: str - - - .. py:attribute:: cascade_configuration - :type: CascadeConfiguration - - - .. py:attribute:: referencing_attribute - :type: Optional[str] - :value: None - - - - .. py:attribute:: additional_properties - :type: Optional[Dict[str, Any]] - :value: None - - - - .. py:method:: to_dict() -> Dict[str, Any] - - Convert to Web API JSON format. - - Example:: - - >>> rel = OneToManyRelationshipMetadata( - ... schema_name="new_account_orders", - ... referenced_entity="account", - ... referencing_entity="new_order", - ... referenced_attribute="accountid" - ... ) - >>> rel.to_dict() - { - '@odata.type': 'Microsoft.Dynamics.CRM.OneToManyRelationshipMetadata', - 'SchemaName': 'new_account_orders', - 'ReferencedEntity': 'account', - 'ReferencingEntity': 'new_order', - 'ReferencedAttribute': 'accountid', - 'CascadeConfiguration': {...} - } - - - -.. py:class:: ManyToManyRelationshipMetadata - - Metadata for a many-to-many entity relationship. - - :param schema_name: Schema name for the relationship. - :type schema_name: str - :param entity1_logical_name: Logical name of the first entity. - :type entity1_logical_name: str - :param entity2_logical_name: Logical name of the second entity. - :type entity2_logical_name: str - :param intersect_entity_name: Name for the intersect table (defaults to schema_name if not provided). - :type intersect_entity_name: Optional[str] - :param additional_properties: Optional dict of additional properties to include - in the Web API payload. Useful for setting inherited properties like - "IsValidForAdvancedFind", "IsCustomizable", "SecurityTypes", or direct - properties like "Entity1NavigationPropertyName". These are merged last - and can override default values. - :type additional_properties: Optional[Dict[str, Any]] - - - .. py:attribute:: schema_name - :type: str - - - .. py:attribute:: entity1_logical_name - :type: str - - - .. py:attribute:: entity2_logical_name - :type: str - - - .. py:attribute:: intersect_entity_name - :type: Optional[str] - :value: None - - - - .. py:attribute:: additional_properties - :type: Optional[Dict[str, Any]] - :value: None - - - - .. py:method:: to_dict() -> Dict[str, Any] - - Convert to Web API JSON format. - - Example:: - - >>> rel = ManyToManyRelationshipMetadata( - ... schema_name="new_account_contact", - ... entity1_logical_name="account", - ... entity2_logical_name="contact" - ... ) - >>> rel.to_dict() - { - '@odata.type': 'Microsoft.Dynamics.CRM.ManyToManyRelationshipMetadata', - 'SchemaName': 'new_account_contact', - 'Entity1LogicalName': 'account', - 'Entity2LogicalName': 'contact', - 'IntersectEntityName': 'new_account_contact' - } - - - -.. py:class:: RelationshipInfo - - Typed return model for relationship metadata. - - Returned by :meth:`~PowerPlatform.Dataverse.operations.tables.TableOperations.create_one_to_many_relationship`, - :meth:`~PowerPlatform.Dataverse.operations.tables.TableOperations.create_many_to_many_relationship`, - :meth:`~PowerPlatform.Dataverse.operations.tables.TableOperations.get_relationship`, and - :meth:`~PowerPlatform.Dataverse.operations.tables.TableOperations.create_lookup_field`. - - :param relationship_id: Relationship metadata GUID. - :type relationship_id: :class:`str` or None - :param relationship_schema_name: Relationship schema name. - :type relationship_schema_name: :class:`str` - :param relationship_type: Either ``"one_to_many"`` or ``"many_to_many"``. - :type relationship_type: :class:`str` - :param lookup_schema_name: Lookup field schema name (one-to-many only). - :type lookup_schema_name: :class:`str` or None - :param referenced_entity: Parent entity logical name (one-to-many only). - :type referenced_entity: :class:`str` or None - :param referencing_entity: Child entity logical name (one-to-many only). - :type referencing_entity: :class:`str` or None - :param entity1_logical_name: First entity logical name (many-to-many only). - :type entity1_logical_name: :class:`str` or None - :param entity2_logical_name: Second entity logical name (many-to-many only). - :type entity2_logical_name: :class:`str` or None - - Example:: - - result = client.tables.create_one_to_many_relationship(lookup, relationship) - print(result.relationship_schema_name) - print(result.lookup_schema_name) - - - .. py:attribute:: relationship_id - :type: Optional[str] - :value: None - - - - .. py:attribute:: relationship_schema_name - :type: str - :value: '' - - - - .. py:attribute:: relationship_type - :type: str - :value: '' - - - - .. py:attribute:: lookup_schema_name - :type: Optional[str] - :value: None - - - - .. py:attribute:: referenced_entity - :type: Optional[str] - :value: None - - - - .. py:attribute:: referencing_entity - :type: Optional[str] - :value: None - - - - .. py:attribute:: entity1_logical_name - :type: Optional[str] - :value: None - - - - .. py:attribute:: entity2_logical_name - :type: Optional[str] - :value: None - - - - .. py:method:: from_one_to_many(*, relationship_id: Optional[str], relationship_schema_name: str, lookup_schema_name: str, referenced_entity: str, referencing_entity: str) -> RelationshipInfo - :classmethod: - - - Create from a one-to-many relationship result. - - :param relationship_id: Relationship metadata GUID. - :type relationship_id: :class:`str` or None - :param relationship_schema_name: Relationship schema name. - :type relationship_schema_name: :class:`str` - :param lookup_schema_name: Lookup field schema name. - :type lookup_schema_name: :class:`str` - :param referenced_entity: Parent entity logical name. - :type referenced_entity: :class:`str` - :param referencing_entity: Child entity logical name. - :type referencing_entity: :class:`str` - :rtype: :class:`RelationshipInfo` - - - - .. py:method:: from_many_to_many(*, relationship_id: Optional[str], relationship_schema_name: str, entity1_logical_name: str, entity2_logical_name: str) -> RelationshipInfo - :classmethod: - - - Create from a many-to-many relationship result. - - :param relationship_id: Relationship metadata GUID. - :type relationship_id: :class:`str` or None - :param relationship_schema_name: Relationship schema name. - :type relationship_schema_name: :class:`str` - :param entity1_logical_name: First entity logical name. - :type entity1_logical_name: :class:`str` - :param entity2_logical_name: Second entity logical name. - :type entity2_logical_name: :class:`str` - :rtype: :class:`RelationshipInfo` - - - - .. py:method:: from_api_response(response_data: Dict[str, Any]) -> RelationshipInfo - :classmethod: - - - Create from a raw Dataverse Web API response. - - Detects one-to-many vs many-to-many from the ``@odata.type`` field - in the response and maps PascalCase keys to snake_case attributes. - Dataverse only supports these two relationship types; an unrecognized - ``@odata.type`` raises :class:`ValueError`. - - :param response_data: Raw relationship metadata from the Web API. - :type response_data: :class:`dict` - :rtype: :class:`RelationshipInfo` - :raises ValueError: If the ``@odata.type`` is not a recognized - relationship type. - - - diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/models/table_info/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/models/table_info/index.rst deleted file mode 100644 index fd73d890..00000000 --- a/docs_local/autoapi/PowerPlatform/Dataverse/models/table_info/index.rst +++ /dev/null @@ -1,305 +0,0 @@ -PowerPlatform.Dataverse.models.table_info -========================================= - -.. py:module:: PowerPlatform.Dataverse.models.table_info - -.. autoapi-nested-parse:: - - Table and column metadata models for Dataverse. - - - -Classes -------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.models.table_info.ColumnInfo - PowerPlatform.Dataverse.models.table_info.TableInfo - PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo - - -Module Contents ---------------- - -.. py:class:: ColumnInfo - - Column metadata from a Dataverse table definition. - - :param schema_name: Column schema name (e.g. ``"new_Price"``). - :type schema_name: :class:`str` - :param logical_name: Column logical name (lowercase). - :type logical_name: :class:`str` - :param type: Column type string (e.g. ``"String"``, ``"Integer"``). - :type type: :class:`str` - :param is_primary: Whether this is the primary name column. - :type is_primary: :class:`bool` - :param is_required: Whether the column is required. - :type is_required: :class:`bool` - :param max_length: Maximum length for string columns. - :type max_length: :class:`int` or None - :param display_name: Human-readable display name. - :type display_name: :class:`str` or None - :param description: Column description. - :type description: :class:`str` or None - - - .. py:attribute:: schema_name - :type: str - :value: '' - - - - .. py:attribute:: logical_name - :type: str - :value: '' - - - - .. py:attribute:: type - :type: str - :value: '' - - - - .. py:attribute:: is_primary - :type: bool - :value: False - - - - .. py:attribute:: is_required - :type: bool - :value: False - - - - .. py:attribute:: max_length - :type: Optional[int] - :value: None - - - - .. py:attribute:: display_name - :type: Optional[str] - :value: None - - - - .. py:attribute:: description - :type: Optional[str] - :value: None - - - - .. py:method:: from_api_response(response_data: Dict[str, Any]) -> ColumnInfo - :classmethod: - - - Create from a raw Dataverse ``AttributeMetadata`` API response. - - :param response_data: Raw attribute metadata dict (PascalCase keys). - :type response_data: :class:`dict` - :rtype: :class:`ColumnInfo` - - - -.. py:class:: TableInfo - - Table metadata with dict-like backward compatibility. - - Supports both new attribute access (``info.schema_name``) and legacy - dict-key access (``info["table_schema_name"]``) for backward - compatibility with code written against the raw dict API. - - :param schema_name: Table schema name (e.g. ``"Account"``). - :type schema_name: :class:`str` - :param logical_name: Table logical name (lowercase). - :type logical_name: :class:`str` - :param entity_set_name: OData entity set name. - :type entity_set_name: :class:`str` - :param metadata_id: Metadata GUID. - :type metadata_id: :class:`str` - :param display_name: Human-readable display name. - :type display_name: :class:`str` or None - :param description: Table description. - :type description: :class:`str` or None - :param columns: Column metadata (when retrieved). - :type columns: list[ColumnInfo] or None - :param columns_created: Column schema names created with the table. - :type columns_created: list[str] or None - - Example:: - - info = client.tables.create("new_Product", {"new_Price": "decimal"}) - print(info.schema_name) # new attribute access - print(info["table_schema_name"]) # legacy dict-key access - - - .. py:attribute:: schema_name - :type: str - :value: '' - - - - .. py:attribute:: logical_name - :type: str - :value: '' - - - - .. py:attribute:: entity_set_name - :type: str - :value: '' - - - - .. py:attribute:: metadata_id - :type: str - :value: '' - - - - .. py:attribute:: primary_name_attribute - :type: Optional[str] - :value: None - - - - .. py:attribute:: primary_id_attribute - :type: Optional[str] - :value: None - - - - .. py:attribute:: display_name - :type: Optional[str] - :value: None - - - - .. py:attribute:: description - :type: Optional[str] - :value: None - - - - .. py:attribute:: columns - :type: Optional[List[ColumnInfo]] - :value: None - - - - .. py:attribute:: columns_created - :type: Optional[List[str]] - :value: None - - - - .. py:method:: get(key: str, default: Any = None) -> Any - - Return value for *key*, or *default* if not present. - - - - .. py:method:: keys() -> KeysView[str] - - Return legacy dict keys. - - - - .. py:method:: values() -> List[Any] - - Return values corresponding to legacy dict keys. - - - - .. py:method:: items() -> List[tuple] - - Return (legacy_key, value) pairs. - - - - .. py:method:: from_dict(data: Dict[str, Any]) -> TableInfo - :classmethod: - - - Create from an SDK internal dict (snake_case keys). - - This handles the dict format returned by ``_create_table`` and - ``_get_table_info`` in the OData layer. - - :param data: Dictionary with SDK snake_case keys. - :type data: :class:`dict` - :rtype: :class:`TableInfo` - - - - .. py:method:: from_api_response(response_data: Dict[str, Any]) -> TableInfo - :classmethod: - - - Create from a raw Dataverse ``EntityDefinition`` API response. - - :param response_data: Raw entity metadata dict (PascalCase keys). - :type response_data: :class:`dict` - :rtype: :class:`TableInfo` - - - - .. py:method:: to_dict() -> Dict[str, Any] - - Return a dict with legacy keys for backward compatibility. - - - -.. py:class:: AlternateKeyInfo - - Alternate key metadata for a Dataverse table. - - :param metadata_id: Key metadata GUID. - :type metadata_id: :class:`str` - :param schema_name: Key schema name. - :type schema_name: :class:`str` - :param key_attributes: List of column logical names that compose the key. - :type key_attributes: list[str] - :param status: Index creation status (``"Active"``, ``"Pending"``, ``"InProgress"``, ``"Failed"``). - :type status: :class:`str` - - - .. py:attribute:: metadata_id - :type: str - :value: '' - - - - .. py:attribute:: schema_name - :type: str - :value: '' - - - - .. py:attribute:: key_attributes - :type: List[str] - :value: [] - - - - .. py:attribute:: status - :type: str - :value: '' - - - - .. py:method:: from_api_response(response_data: Dict[str, Any]) -> AlternateKeyInfo - :classmethod: - - - Create from raw EntityKeyMetadata API response. - - :param response_data: Raw key metadata dictionary from the Web API. - :type response_data: :class:`dict` - :rtype: :class:`AlternateKeyInfo` - - - diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/models/upsert/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/models/upsert/index.rst deleted file mode 100644 index b6568676..00000000 --- a/docs_local/autoapi/PowerPlatform/Dataverse/models/upsert/index.rst +++ /dev/null @@ -1,54 +0,0 @@ -PowerPlatform.Dataverse.models.upsert -===================================== - -.. py:module:: PowerPlatform.Dataverse.models.upsert - -.. autoapi-nested-parse:: - - Upsert data models for the Dataverse SDK. - - - -Classes -------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.models.upsert.UpsertItem - - -Module Contents ---------------- - -.. py:class:: UpsertItem - - Represents a single upsert operation targeting a record by its alternate key. - - Used with :meth:`~PowerPlatform.Dataverse.operations.records.RecordOperations.upsert` - to upsert one or more records identified by alternate keys rather than primary GUIDs. - - :param alternate_key: Dictionary mapping alternate key attribute names to their values. - String values are automatically quoted and escaped in the OData URL. Integer and - other non-string values are included without quotes. - :type alternate_key: dict[str, Any] - :param record: Dictionary of attribute names to values for the record payload. - Keys are automatically lowercased. Picklist labels are resolved to integer option - values when a matching option set is found. - :type record: dict[str, Any] - - Example:: - - item = UpsertItem( - alternate_key={"accountnumber": "ACC-001", "address1_postalcode": "98052"}, - record={"name": "Contoso Ltd", "telephone1": "555-0100"}, - ) - - - .. py:attribute:: alternate_key - :type: Dict[str, Any] - - - .. py:attribute:: record - :type: Dict[str, Any] - - diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/operations/batch/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/operations/batch/index.rst deleted file mode 100644 index 160530b7..00000000 --- a/docs_local/autoapi/PowerPlatform/Dataverse/operations/batch/index.rst +++ /dev/null @@ -1,741 +0,0 @@ -PowerPlatform.Dataverse.operations.batch -======================================== - -.. py:module:: PowerPlatform.Dataverse.operations.batch - -.. autoapi-nested-parse:: - - Batch operation namespaces for the Dataverse SDK. - - - -Classes -------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.operations.batch.ChangeSetRecordOperations - PowerPlatform.Dataverse.operations.batch.ChangeSet - PowerPlatform.Dataverse.operations.batch.BatchRecordOperations - PowerPlatform.Dataverse.operations.batch.BatchTableOperations - PowerPlatform.Dataverse.operations.batch.BatchQueryOperations - PowerPlatform.Dataverse.operations.batch.BatchDataFrameOperations - PowerPlatform.Dataverse.operations.batch.BatchRequest - PowerPlatform.Dataverse.operations.batch.BatchOperations - - -Module Contents ---------------- - -.. py:class:: ChangeSetRecordOperations(cs_internal: PowerPlatform.Dataverse.data._batch._ChangeSet) - - Record write operations available inside a :class:`ChangeSet`. - - Mirrors ``client.records`` but restricted to single-record forms (no bulk - create/update/delete). Only write operations are allowed — GET is not - permitted inside a changeset. - - Do not instantiate directly; use ``ChangeSet.records``. - - - .. py:method:: create(table: str, data: Dict[str, Any]) -> str - - Add a single-record create to this changeset. - - :param table: Table schema name (e.g. ``"account"``). - :type table: :class:`str` - :param data: Column values for the new record. - :type data: dict[str, typing.Any] - :returns: A content-ID reference string (e.g. ``"$1"``) usable in - subsequent operations within this changeset as a URI reference - in ``@odata.bind`` fields or as ``record_id`` in - :meth:`update` / :meth:`delete`. - :rtype: :class:`str` - - Example:: - - with batch.changeset() as cs: - lead_ref = cs.records.create("lead", {"firstname": "Ada"}) - cs.records.create("account", { - "name": "Babbage", - "originatingleadid@odata.bind": lead_ref, - }) - - - - .. py:method:: update(table: str, record_id: str, changes: Dict[str, Any]) -> None - - Add a single-record update to this changeset. - - :param table: Table schema name. Ignored when ``record_id`` is a - content-ID reference. - :type table: :class:`str` - :param record_id: GUID or a content-ID reference (e.g. ``"$1"``) - returned by a prior :meth:`create` in this changeset. - :type record_id: :class:`str` - :param changes: Column values to update. - :type changes: dict[str, typing.Any] - - - - .. py:method:: delete(table: str, record_id: str) -> None - - Add a single-record delete to this changeset. - - :param table: Table schema name. Ignored when ``record_id`` is a - content-ID reference. - :type table: :class:`str` - :param record_id: GUID or a content-ID reference (e.g. ``"$1"``). - :type record_id: :class:`str` - - - -.. py:class:: ChangeSet(internal: PowerPlatform.Dataverse.data._batch._ChangeSet) - - A transactional group of single-record write operations. - - All operations succeed or are rolled back together. Use as a context - manager or call ``records`` to add operations directly. - - Do not instantiate directly; use :meth:`BatchRequest.changeset`. - - Example:: - - with batch.changeset() as cs: - ref = cs.records.create("contact", {"firstname": "Alice"}) - cs.records.update("account", account_id, { - "primarycontactid@odata.bind": ref - }) - - - .. py:attribute:: records - - -.. py:class:: BatchRecordOperations(batch: _BatchContext) - - Record operations on a :class:`BatchRequest`. - - Mirrors ``client.records``: same method names, same signatures. - All methods return ``None``; results are available via - :class:`~PowerPlatform.Dataverse.models.batch.BatchResult` after - :meth:`BatchRequest.execute`. - - GA methods: :meth:`retrieve` (single record) and :meth:`list` (multi-record, - single page). :meth:`get` is deprecated — use :meth:`retrieve` instead. - - Do not instantiate directly; use ``batch.records``. - - - .. py:method:: create(table: str, data: Union[Dict[str, Any], List[Dict[str, Any]]]) -> None - - Add a create operation to the batch. - - A single dict creates one record (POST entity_set). - A list of dicts creates all records via the ``CreateMultiple`` action - (one batch item). - - :param table: Table schema name (e.g. ``"account"``). - :type table: :class:`str` - :param data: Single record dict or list of record dicts. - :type data: dict or list[dict] - - - - .. py:method:: update(table: str, ids: Union[str, List[str]], changes: Union[Dict[str, Any], List[Dict[str, Any]]]) -> None - - Add an update operation to the batch. - - - **Single** ``(table, "guid", {...})`` -> one PATCH request. - - **Broadcast** ``(table, [id1, id2], {...})`` -> one ``UpdateMultiple`` POST. - - **Paired** ``(table, [id1, id2], [{...}, {...}])`` -> one ``UpdateMultiple`` POST. - - :param table: Table schema name. - :type table: :class:`str` - :param ids: Single GUID or list of GUIDs. - :type ids: str or list[str] - :param changes: Single dict (single/broadcast) or list of dicts (paired). - :type changes: dict or list[dict] - - - - .. py:method:: delete(table: str, ids: Union[str, List[str]], *, use_bulk_delete: bool = True) -> None - - Add a delete operation to the batch. - - - **Single** ``(table, "guid")`` -> one DELETE request. - - **List + use_bulk_delete=True** (default) -> one ``BulkDelete`` POST. - The async job ID will be available in ``BatchItemResponse.data["JobId"]``. - - **List + use_bulk_delete=False** -> one DELETE per record. - - :param table: Table schema name. - :type table: :class:`str` - :param ids: Single GUID or list of GUIDs. - :type ids: str or list[str] - :param use_bulk_delete: When True (default) and ``ids`` is a list, use the - BulkDelete action. When False, delete records individually. - :type use_bulk_delete: :class:`bool` - - - - .. py:method:: get(table: str, record_id: str, *, select: Optional[List[str]] = None) -> None - - Add a single-record get operation to the batch. - - .. deprecated:: - Use :meth:`retrieve` instead. ``batch.records.get()`` is deprecated - and will be removed in a future release. - - :param table: Table schema name. - :type table: :class:`str` - :param record_id: GUID of the record to retrieve. - :type record_id: :class:`str` - :param select: Optional list of column names to include. - :type select: list[str] or None - - - - .. py:method:: upsert(table: str, items: List[Union[PowerPlatform.Dataverse.models.upsert.UpsertItem, Dict[str, Any]]]) -> None - - Add an upsert operation to the batch. - - Mirrors :meth:`~PowerPlatform.Dataverse.operations.records.RecordOperations.upsert`: - a single item becomes a PATCH request using the alternate key; multiple items - become one ``UpsertMultiple`` POST. - - Each item must be a :class:`~PowerPlatform.Dataverse.models.upsert.UpsertItem` - or a plain ``dict`` with ``"alternate_key"`` and ``"record"`` keys (both dicts). - - :param table: Table schema name (e.g. ``"account"``). - :type table: :class:`str` - :param items: Non-empty list of :class:`~PowerPlatform.Dataverse.models.upsert.UpsertItem` - instances or equivalent dicts. - :type items: list[~PowerPlatform.Dataverse.models.upsert.UpsertItem] - - :raises TypeError: If ``items`` is not a non-empty list, or if any element is - neither a :class:`~PowerPlatform.Dataverse.models.upsert.UpsertItem` nor a - dict with ``"alternate_key"`` and ``"record"`` keys. - - Example:: - - from PowerPlatform.Dataverse.models import UpsertItem - - batch.records.upsert("account", [ - UpsertItem( - alternate_key={"accountnumber": "ACC-001"}, - record={"name": "Contoso Ltd"}, - ), - UpsertItem( - alternate_key={"accountnumber": "ACC-002"}, - record={"name": "Fabrikam Inc"}, - ), - ]) - - - - .. py:method:: retrieve(table: str, record_id: str, *, select: Optional[List[str]] = None, expand: Optional[List[str]] = None, include_annotations: Optional[str] = None) -> None - - Add a single-record retrieve operation to the batch. - - GA replacement for the deprecated :meth:`get`. Enqueues a GET request - for one record by its GUID. The response body will be available in - :attr:`~PowerPlatform.Dataverse.models.batch.BatchItemResponse.data` - after :meth:`BatchRequest.execute`. - - :param table: Table schema name (e.g. ``"account"``). - :type table: :class:`str` - :param record_id: GUID of the record to retrieve. - :type record_id: :class:`str` - :param select: Optional list of column logical names to include. - :type select: list[str] or None - :param expand: Optional list of navigation properties to expand. - Navigation property names are case-sensitive and must match the - entity's ``$metadata``. - :type expand: list[str] or None - :param include_annotations: OData annotation pattern for the - ``Prefer: odata.include-annotations`` header (e.g. ``"*"`` or - ``"OData.Community.Display.V1.FormattedValue"``), or ``None``. - :type include_annotations: :class:`str` or None - - Example:: - - batch = client.batch.new() - batch.records.retrieve( - "account", account_id, - select=["name", "statuscode"], - expand=["primarycontactid"], - include_annotations="OData.Community.Display.V1.FormattedValue", - ) - result = batch.execute() - record = result.responses[0].data - contact = (record.get("primarycontactid") or {}) - print(contact.get("fullname")) - - - - .. py:method:: list(table: str, *, filter: Optional[Union[str, FilterExpression]] = None, select: Optional[List[str]] = None, orderby: Optional[List[str]] = None, top: Optional[int] = None, expand: Optional[List[str]] = None, page_size: Optional[int] = None, count: bool = False, include_annotations: Optional[str] = None) -> None - - Add a multi-record list operation to the batch (single page, no pagination). - - Enqueues a GET request for multiple records. Because batch requests are - a single HTTP round-trip, pagination (``@odata.nextLink``) is **not** - supported — use ``top`` to bound the result size, or rely on the - server's default page limit. - - The response body (``{"value": [...]}`` JSON) will be available in - :attr:`~PowerPlatform.Dataverse.models.batch.BatchItemResponse.data` - after :meth:`BatchRequest.execute`. - - :param table: Table schema name (e.g. ``"account"``). - :type table: :class:`str` - :param filter: Optional OData ``$filter`` expression or :class:`~PowerPlatform.Dataverse.models.filters.FilterExpression`. - :type filter: str or FilterExpression or None - :param select: Optional list of column logical names to include. - :type select: list[str] or None - :param orderby: Optional list of sort expressions (e.g. ``["name asc"]``). - :type orderby: list[str] or None - :param top: Maximum number of records to return. - :type top: int or None - :param expand: Optional list of navigation properties to expand. - :type expand: list[str] or None - :param page_size: Per-page size hint via ``Prefer: odata.maxpagesize``. - :type page_size: int or None - :param count: If ``True``, adds ``$count=true`` to the request. - :type count: bool - :param include_annotations: OData annotation pattern for the - ``Prefer: odata.include-annotations`` header, or ``None``. - :type include_annotations: :class:`str` or None - - Example:: - - batch = client.batch.new() - batch.records.list( - "account", - filter="statecode eq 0", - select=["name", "statuscode"], - orderby=["name asc"], - top=50, - include_annotations="OData.Community.Display.V1.FormattedValue", - ) - result = batch.execute() - records = result.responses[0].data.get("value", []) - - - -.. py:class:: BatchTableOperations(batch: _BatchContext) - - Table metadata operations on a :class:`BatchRequest`. - - Mirrors ``client.tables`` exactly: same method names, same signatures. - All methods return ``None``; results arrive via - :class:`~PowerPlatform.Dataverse.models.batch.BatchResult`. - - .. note:: - ``tables.delete``, ``tables.add_columns``, and ``tables.remove_columns`` - require a metadata lookup (GET ``EntityDefinitions``) at - :meth:`BatchRequest.execute` time to resolve the table's MetadataId. - This lookup is transparent to the caller. - - .. note:: - ``tables.add_columns`` and ``tables.remove_columns`` each produce one - batch item per column, so they contribute multiple entries to - :attr:`~PowerPlatform.Dataverse.models.batch.BatchResult.responses`. - - Do not instantiate directly; use ``batch.tables``. - - - .. py:method:: create(table: str, columns: Dict[str, Any], *, solution: Optional[str] = None, primary_column: Optional[str] = None, display_name: Optional[str] = None) -> None - - Add a table-create operation to the batch. - - .. note:: - The pre-existence check performed by ``client.tables.create`` is skipped - in batch mode. If the table already exists the server returns an error - in the corresponding :class:`~PowerPlatform.Dataverse.models.batch.BatchItemResponse`. - - :param table: Schema name of the new table (e.g. ``"new_Product"``). - :type table: :class:`str` - :param columns: Mapping of column schema names to type strings or Enum subclasses. - :type columns: dict[str, typing.Any] - :param solution: Optional solution unique name. - :type solution: str or None - :param primary_column: Optional primary column schema name. - :type primary_column: str or None - :param display_name: Human-readable display name for the table. - When omitted, defaults to the table schema name. - :type display_name: str or None - - - - .. py:method:: delete(table: str) -> None - - Add a table-delete operation to the batch. - - The table's ``MetadataId`` is resolved via a GET request at execute time. - - :param table: Schema name of the table to delete. - :type table: :class:`str` - - - - .. py:method:: get(table: str) -> None - - Add a table-metadata-get operation to the batch. - - The response will be in ``BatchItemResponse.data`` after execute. - - :param table: Schema name of the table. - :type table: :class:`str` - - - - .. py:method:: list(*, filter: Optional[str] = None, select: Optional[List[str]] = None) -> None - - Add a list-all-tables operation to the batch. - - Mirrors ``client.tables.list()``. Supply an optional OData - ``$filter`` expression to further narrow the results (combined with - ``IsPrivate eq false`` using ``and``). ``select`` projects - specific property names via ``$select``. - - The response will be in ``BatchItemResponse.data`` after execute. - - :param filter: Additional OData ``$filter`` expression. - :type filter: str or None - :param select: List of property names for ``$select``. - :type select: list[str] or None - - - - .. py:method:: add_columns(table: str, columns: Dict[str, Any]) -> None - - Add column-create operations to the batch (one per column). - - The table's ``MetadataId`` is resolved at execute time. Each column - produces one entry in :attr:`~PowerPlatform.Dataverse.models.batch.BatchResult.responses`. - - :param table: Schema name of the target table. - :type table: :class:`str` - :param columns: Mapping of column schema names to type strings or Enum subclasses. - :type columns: dict[str, typing.Any] - - - - .. py:method:: remove_columns(table: str, columns: Union[str, List[str]]) -> None - - Add column-delete operations to the batch (one per column). - - The table's ``MetadataId`` and each column's ``MetadataId`` are resolved - at execute time. Each column produces one entry in - :attr:`~PowerPlatform.Dataverse.models.batch.BatchResult.responses`. - - :param table: Schema name of the target table. - :type table: :class:`str` - :param columns: Column schema name or list of column schema names to remove. - :type columns: str or list[str] - - - - .. py:method:: create_one_to_many_relationship(lookup: PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata, relationship: PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata, *, solution: Optional[str] = None) -> None - - Add a one-to-many relationship creation to the batch. - - :param lookup: Lookup attribute metadata. - :type lookup: ~PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata - :param relationship: Relationship metadata. - :type relationship: ~PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata - :param solution: Optional solution unique name. - :type solution: str or None - - - - .. py:method:: create_many_to_many_relationship(relationship: PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata, *, solution: Optional[str] = None) -> None - - Add a many-to-many relationship creation to the batch. - - :param relationship: Relationship metadata. - :type relationship: ~PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata - :param solution: Optional solution unique name. - :type solution: str or None - - - - .. py:method:: delete_relationship(relationship_id: str) -> None - - Add a relationship-delete operation to the batch. - - :param relationship_id: GUID of the relationship metadata to delete. - :type relationship_id: :class:`str` - - - - .. py:method:: get_relationship(schema_name: str) -> None - - Add a relationship-metadata-get operation to the batch. - - The response will be in ``BatchItemResponse.data`` after execute. - - :param schema_name: Schema name of the relationship. - :type schema_name: :class:`str` - - - - .. py:method:: create_lookup_field(referencing_table: str, lookup_field_name: str, referenced_table: str, *, display_name: Optional[str] = None, description: Optional[str] = None, required: bool = False, cascade_delete: str = CASCADE_BEHAVIOR_REMOVE_LINK, solution: Optional[str] = None, language_code: int = 1033) -> None - - Add a lookup field creation to the batch (convenience wrapper for - :meth:`create_one_to_many_relationship`). - - :param referencing_table: Logical name of the child (many) table. - :type referencing_table: :class:`str` - :param lookup_field_name: Schema name for the lookup field. - :type lookup_field_name: :class:`str` - :param referenced_table: Logical name of the parent (one) table. - :type referenced_table: :class:`str` - :param display_name: Display name for the lookup field. - :type display_name: str or None - :param description: Optional description. - :type description: str or None - :param required: Whether the lookup is required. - :type required: :class:`bool` - :param cascade_delete: Delete cascade behaviour. - :type cascade_delete: :class:`str` - :param solution: Optional solution unique name. - :type solution: str or None - :param language_code: Language code for labels (default 1033). - :type language_code: :class:`int` - - - -.. py:class:: BatchQueryOperations(batch: _BatchContext) - - Query operations on a :class:`BatchRequest`. - - Mirrors ``client.query`` exactly: same method names, same signatures. - All methods return ``None``; results arrive via - :class:`~PowerPlatform.Dataverse.models.batch.BatchResult`. - - Do not instantiate directly; use ``batch.query``. - - - .. py:method:: sql(sql: str) -> None - - Add a SQL SELECT query to the batch. - - Mirrors :meth:`~PowerPlatform.Dataverse.operations.query.QueryOperations.sql`. - The entity set is resolved from the table name in the SQL statement at - :meth:`BatchRequest.execute` time. - - :param sql: A single ``SELECT`` statement within the Dataverse-supported subset. - :type sql: :class:`str` - - :raises ~PowerPlatform.Dataverse.core.errors.ValidationError: - If ``sql`` is not a non-empty string. - - Example:: - - batch.query.sql("SELECT accountid, name FROM account WHERE name = 'Contoso'") - - - -.. py:class:: BatchDataFrameOperations(batch: _BatchContext) - - DataFrame-oriented wrappers for batch record operations. - - Provides :meth:`create`, :meth:`update`, and :meth:`delete` that accept - ``pandas.DataFrame`` / ``pandas.Series`` inputs and convert them to standard - dicts before enqueueing on the batch. This lets data-science callers feed - DataFrames directly into a batch without manual conversion. - - Accessed via ``batch.dataframe``. - - Example:: - - import pandas as pd - - batch = client.batch.new() - df = pd.DataFrame([ - {"name": "Contoso", "telephone1": "555-0100"}, - {"name": "Fabrikam", "telephone1": "555-0200"}, - ]) - batch.dataframe.create("account", df) - result = batch.execute() - - - .. py:method:: create(table: str, records: pandas.DataFrame) -> None - - Enqueue record creates from a pandas DataFrame. - - Each row becomes a record. All rows are bundled in a single - ``CreateMultiple`` batch item (one HTTP request in the batch). - - :param table: Table schema name (e.g. ``"account"``). - :type table: :class:`str` - :param records: DataFrame where each row is a record to create. - :type records: ~pandas.DataFrame - - :raises TypeError: If ``records`` is not a pandas DataFrame. - :raises ValueError: If ``records`` is empty or any row has no non-null values. - - Example:: - - df = pd.DataFrame([{"name": "Contoso"}, {"name": "Fabrikam"}]) - batch.dataframe.create("account", df) - - - - .. py:method:: update(table: str, changes: pandas.DataFrame, id_column: str, clear_nulls: bool = False) -> None - - Enqueue record updates from a pandas DataFrame. - - Each row represents an update. The ``id_column`` specifies which - column contains the record GUIDs. - - :param table: Table schema name (e.g. ``"account"``). - :type table: :class:`str` - :param changes: DataFrame where each row contains a record GUID and - the fields to update. - :type changes: ~pandas.DataFrame - :param id_column: Name of the DataFrame column containing record GUIDs. - :type id_column: :class:`str` - :param clear_nulls: When ``False`` (default), NaN/None values are - skipped. When ``True``, NaN/None sends ``null`` to clear the field. - :type clear_nulls: :class:`bool` - - :raises TypeError: If ``changes`` is not a pandas DataFrame. - :raises ValueError: If ``changes`` is empty, ``id_column`` is missing, - or IDs are invalid. - - Example:: - - df = pd.DataFrame([ - {"accountid": "guid-1", "telephone1": "555-0100"}, - {"accountid": "guid-2", "telephone1": "555-0200"}, - ]) - batch.dataframe.update("account", df, id_column="accountid") - - - - .. py:method:: delete(table: str, ids: pandas.Series, use_bulk_delete: bool = True) -> None - - Enqueue record deletes from a pandas Series of GUIDs. - - :param table: Table schema name (e.g. ``"account"``). - :type table: :class:`str` - :param ids: Series of record GUIDs to delete. - :type ids: ~pandas.Series - :param use_bulk_delete: When ``True`` (default) and ``ids`` has multiple - values, use the ``BulkDelete`` action. - :type use_bulk_delete: :class:`bool` - - :raises TypeError: If ``ids`` is not a pandas Series. - :raises ValueError: If ``ids`` contains invalid values. - - Example:: - - ids_series = pd.Series(["guid-1", "guid-2", "guid-3"]) - batch.dataframe.delete("account", ids_series) - - - -.. py:class:: BatchRequest(client: PowerPlatform.Dataverse.client.DataverseClient) - - Builder for constructing and executing a Dataverse OData ``$batch`` request. - - Obtain via :meth:`BatchOperations.new` (``client.batch.new()``). Add operations - through ``records``, ``tables``, ``query``, and ``dataframe``, - optionally group writes - into a :meth:`changeset`, then call :meth:`execute`. - - Operations are executed sequentially in the order added. The resulting - :class:`~PowerPlatform.Dataverse.models.batch.BatchResult` contains one - :class:`~PowerPlatform.Dataverse.models.batch.BatchItemResponse` per HTTP - request dispatched (some operations expand to multiple requests). - - .. note:: - Maximum 1000 HTTP operations per batch. - - Example:: - - batch = client.batch.new() - batch.records.create("account", {"name": "Contoso"}) - batch.tables.get("account") - with batch.changeset() as cs: - ref = cs.records.create("contact", {"firstname": "Alice"}) - cs.records.update("account", account_id, { - "primarycontactid@odata.bind": ref - }) - result = batch.execute() - - - .. py:attribute:: records - - - .. py:attribute:: tables - - - .. py:attribute:: query - - - .. py:attribute:: dataframe - - - .. py:method:: changeset() -> ChangeSet - - Create a new :class:`ChangeSet` attached to this batch. - - The changeset is added to the batch immediately. Operations added to - the returned :class:`ChangeSet` via ``cs.records.*`` execute atomically. - - :returns: A new :class:`ChangeSet` ready to receive operations. - - Example:: - - with batch.changeset() as cs: - cs.records.create("account", {"name": "ACME"}) - cs.records.create("contact", {"firstname": "Bob"}) - - - - .. py:method:: execute(*, continue_on_error: bool = False) -> PowerPlatform.Dataverse.models.batch.BatchResult - - Submit the batch to Dataverse and return all responses. - - :param continue_on_error: When False (default), Dataverse stops at the - first failure and returns that operation's error as a 4xx response. - When True, ``Prefer: odata.continue-on-error`` is sent and all - operations are attempted. - :returns: :class:`~PowerPlatform.Dataverse.models.batch.BatchResult` - with one entry per HTTP operation in submission order. - :raises ValidationError: If the batch exceeds 1000 operations or an - unsupported column type is specified. - :raises MetadataError: If metadata pre-resolution fails (table or - column not found) for ``tables.delete``, ``tables.add_columns``, - or ``tables.remove_columns``. - :raises HttpError: On HTTP-level failures (auth, server error, etc.) - that prevent the batch from executing. - - - -.. py:class:: BatchOperations(client: PowerPlatform.Dataverse.client.DataverseClient) - - Namespace for batch operations (``client.batch``). - - Accessed via ``client.batch``. Use :meth:`new` to create a - :class:`BatchRequest` builder. - - :param client: The parent :class:`~PowerPlatform.Dataverse.client.DataverseClient` instance. - - Example:: - - batch = client.batch.new() - batch.records.create("account", {"name": "Fabrikam"}) - result = batch.execute() - - - .. py:method:: new() -> BatchRequest - - Create a new empty :class:`BatchRequest` builder. - - :returns: An empty :class:`BatchRequest`. - - - diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/operations/dataframe/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/operations/dataframe/index.rst deleted file mode 100644 index d240dee2..00000000 --- a/docs_local/autoapi/PowerPlatform/Dataverse/operations/dataframe/index.rst +++ /dev/null @@ -1,279 +0,0 @@ -PowerPlatform.Dataverse.operations.dataframe -============================================ - -.. py:module:: PowerPlatform.Dataverse.operations.dataframe - -.. autoapi-nested-parse:: - - DataFrame CRUD operations namespace for the Dataverse SDK. - - - -Classes -------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.operations.dataframe.DataFrameOperations - - -Module Contents ---------------- - -.. py:class:: DataFrameOperations(client: PowerPlatform.Dataverse.client.DataverseClient) - - Namespace for pandas DataFrame CRUD operations. - - Accessed via ``client.dataframe``. Provides DataFrame-oriented wrappers - around the record-level CRUD operations. - - :param client: The parent :class:`~PowerPlatform.Dataverse.client.DataverseClient` instance. - :type client: ~PowerPlatform.Dataverse.client.DataverseClient - - Example:: - - import pandas as pd - - client = DataverseClient(base_url, credential) - - # Query records as a DataFrame - df = client.dataframe.get("account", select=["name"], top=100) - - # Create records from a DataFrame - new_df = pd.DataFrame([{"name": "Contoso"}, {"name": "Fabrikam"}]) - new_df["accountid"] = client.dataframe.create("account", new_df) - - # Update records - new_df["telephone1"] = ["555-0100", "555-0200"] - client.dataframe.update("account", new_df, id_column="accountid") - - # Delete records - client.dataframe.delete("account", new_df["accountid"]) - - - .. py:method:: sql(sql: str) -> pandas.DataFrame - - Execute a SQL query and return the results as a pandas DataFrame. - - Delegates to :meth:`~PowerPlatform.Dataverse.operations.query.QueryOperations.sql` - and converts the list of records into a single DataFrame. - - :param sql: Supported SQL SELECT statement. - :type sql: :class:`str` - - :return: DataFrame containing all result rows. Returns an empty - DataFrame when no rows match. - :rtype: ~pandas.DataFrame - - :raises ~PowerPlatform.Dataverse.core.errors.ValidationError: - If ``sql`` is not a string or is empty. - - .. rubric:: Example - - SQL query to DataFrame:: - - df = client.dataframe.sql( - "SELECT TOP 100 name, revenue FROM account " - "WHERE statecode = 0 ORDER BY revenue" - ) - print(f"Got {len(df)} rows") - print(df.head()) - - Aggregate query to DataFrame:: - - df = client.dataframe.sql( - "SELECT a.name, COUNT(c.contactid) as cnt " - "FROM account a " - "JOIN contact c ON a.accountid = c.parentcustomerid " - "GROUP BY a.name" - ) - - - - .. py:method:: get(table: str, record_id: Optional[str] = None, select: Optional[List[str]] = None, filter: Optional[str] = None, orderby: Optional[List[str]] = None, top: Optional[int] = None, expand: Optional[List[str]] = None, page_size: Optional[int] = None, count: bool = False, include_annotations: Optional[str] = None) -> pandas.DataFrame - - Fetch records and return as a single pandas DataFrame. - - When ``record_id`` is provided, returns a single-row DataFrame. - When ``record_id`` is None, internally iterates all pages and returns one - consolidated DataFrame. - - :param table: Schema name of the table (e.g. ``"account"`` or ``"new_MyTestTable"``). - :type table: :class:`str` - :param record_id: Optional GUID to fetch a specific record. If None, queries multiple records. - :type record_id: :class:`str` or None - :param select: Optional list of attribute logical names to retrieve. - :type select: list[str] or None - :param filter: Optional OData filter string. Column names must use exact lowercase logical names. - :type filter: :class:`str` or None - :param orderby: Optional list of attributes to sort by. - :type orderby: list[str] or None - :param top: Optional maximum number of records to return. - :type top: :class:`int` or None - :param expand: Optional list of navigation properties to expand (case-sensitive). - :type expand: list[str] or None - :param page_size: Optional number of records per page for pagination. - :type page_size: :class:`int` or None - :param count: If ``True``, adds ``$count=true`` to include a total - record count in the response. - :type count: :class:`bool` - :param include_annotations: OData annotation pattern for the - ``Prefer: odata.include-annotations`` header (e.g. ``"*"`` or - ``"OData.Community.Display.V1.FormattedValue"``), or ``None``. - :type include_annotations: :class:`str` or None - - :return: DataFrame containing all matching records. Returns an empty DataFrame - when no records match. - :rtype: ~pandas.DataFrame - - :raises ValueError: If ``record_id`` is not a non-empty string, or if - query parameters (``filter``, ``orderby``, ``top``, ``expand``, - ``page_size``) are provided alongside ``record_id``. - - .. tip:: - For large tables, use ``top`` or ``filter`` to limit the result set. - - .. rubric:: Example - - Fetch a single record as a DataFrame:: - - df = client.dataframe.get("account", record_id=account_id, select=["name", "telephone1"]) - print(df) - - Query with filtering:: - - df = client.dataframe.get("account", filter="statecode eq 0", select=["name"]) - print(f"Got {len(df)} active accounts") - - Limit result size:: - - df = client.dataframe.get("account", select=["name"], top=100) - - - - .. py:method:: create(table: str, records: pandas.DataFrame) -> pandas.Series - - Create records from a pandas DataFrame. - - :param table: Schema name of the table (e.g. ``"account"`` or ``"new_MyTestTable"``). - :type table: :class:`str` - :param records: DataFrame where each row is a record to create. - :type records: ~pandas.DataFrame - - :return: Series of created record GUIDs, aligned with the input DataFrame index. - :rtype: ~pandas.Series - - :raises TypeError: If ``records`` is not a pandas DataFrame. - :raises ValueError: If ``records`` is empty or the number of returned - IDs does not match the number of input rows. - - .. tip:: - All rows are sent in a single ``CreateMultiple`` request. For very - large DataFrames, consider splitting into smaller batches to avoid - request timeouts. - - .. rubric:: Example - - Create records from a DataFrame:: - - import pandas as pd - - df = pd.DataFrame([ - {"name": "Contoso", "telephone1": "555-0100"}, - {"name": "Fabrikam", "telephone1": "555-0200"}, - ]) - df["accountid"] = client.dataframe.create("account", df) - - - - .. py:method:: update(table: str, changes: pandas.DataFrame, id_column: str, clear_nulls: bool = False) -> None - - Update records from a pandas DataFrame. - - Each row in the DataFrame represents an update. The ``id_column`` specifies which - column contains the record GUIDs. - - :param table: Schema name of the table (e.g. ``"account"`` or ``"new_MyTestTable"``). - :type table: :class:`str` - :param changes: DataFrame where each row contains a record GUID and the fields to update. - :type changes: ~pandas.DataFrame - :param id_column: Name of the DataFrame column containing record GUIDs. - :type id_column: :class:`str` - :param clear_nulls: When ``False`` (default), missing values (NaN/None) are skipped - (the field is left unchanged on the server). When ``True``, missing values are sent - as ``null`` to Dataverse, clearing the field. Use ``True`` only when you intentionally - want NaN/None values to clear fields. - :type clear_nulls: :class:`bool` - - :raises TypeError: If ``changes`` is not a pandas DataFrame. - :raises ValueError: If ``changes`` is empty, ``id_column`` is not found in the - DataFrame, ``id_column`` contains invalid (non-string, empty, or whitespace-only) - values, or no updatable columns exist besides ``id_column``. - When ``clear_nulls`` is ``False`` (default), rows where all change values - are NaN/None produce empty patches and are silently skipped. If all rows - are skipped, the method returns without making an API call. When - ``clear_nulls`` is ``True``, NaN/None values become explicit nulls, so - rows are never skipped. - - .. tip:: - All rows are sent in a single ``UpdateMultiple`` request (or a - single PATCH for one row). For very large DataFrames, consider - splitting into smaller batches to avoid request timeouts. - - .. rubric:: Example - - Update records with different values per row:: - - import pandas as pd - - df = pd.DataFrame([ - {"accountid": "guid-1", "telephone1": "555-0100"}, - {"accountid": "guid-2", "telephone1": "555-0200"}, - ]) - client.dataframe.update("account", df, id_column="accountid") - - Broadcast the same change to all records:: - - df = pd.DataFrame({"accountid": ["guid-1", "guid-2", "guid-3"]}) - df["websiteurl"] = "https://example.com" - client.dataframe.update("account", df, id_column="accountid") - - Clear a field by setting clear_nulls=True:: - - df = pd.DataFrame([{"accountid": "guid-1", "websiteurl": None}]) - client.dataframe.update("account", df, id_column="accountid", clear_nulls=True) - - - - .. py:method:: delete(table: str, ids: pandas.Series, use_bulk_delete: bool = True) -> Optional[str] - - Delete records by passing a pandas Series of GUIDs. - - :param table: Schema name of the table (e.g. ``"account"`` or ``"new_MyTestTable"``). - :type table: :class:`str` - :param ids: Series of record GUIDs to delete. - :type ids: ~pandas.Series - :param use_bulk_delete: When ``True`` (default) and ``ids`` contains multiple values, execute the BulkDelete - action and return its async job identifier. When ``False`` each record is deleted sequentially. - :type use_bulk_delete: :class:`bool` - - :raises TypeError: If ``ids`` is not a pandas Series. - :raises ValueError: If ``ids`` contains invalid (non-string, empty, or - whitespace-only) values. - - :return: BulkDelete job ID when deleting multiple records via BulkDelete; - ``None`` when deleting a single record, using sequential deletion, or - when ``ids`` is empty. - :rtype: :class:`str` or None - - .. rubric:: Example - - Delete records using a Series:: - - import pandas as pd - - ids = pd.Series(["guid-1", "guid-2", "guid-3"]) - client.dataframe.delete("account", ids) - - - diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/operations/files/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/operations/files/index.rst deleted file mode 100644 index ad07ef55..00000000 --- a/docs_local/autoapi/PowerPlatform/Dataverse/operations/files/index.rst +++ /dev/null @@ -1,99 +0,0 @@ -PowerPlatform.Dataverse.operations.files -======================================== - -.. py:module:: PowerPlatform.Dataverse.operations.files - -.. autoapi-nested-parse:: - - File operations namespace for the Dataverse SDK. - - - -Classes -------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.operations.files.FileOperations - - -Module Contents ---------------- - -.. py:class:: FileOperations(client: PowerPlatform.Dataverse.client.DataverseClient) - - Namespace for file operations. - - Accessed via ``client.files``. Provides file upload operations for - Dataverse file columns. - - :param client: The parent :class:`~PowerPlatform.Dataverse.client.DataverseClient` instance. - :type client: ~PowerPlatform.Dataverse.client.DataverseClient - - Example:: - - client = DataverseClient(base_url, credential) - - client.files.upload( - "account", account_id, "new_Document", "/path/to/file.pdf" - ) - - - .. py:method:: upload(table: str, record_id: str, file_column: str, path: str, *, mode: Optional[str] = None, mime_type: Optional[str] = None, if_none_match: bool = True) -> None - - Upload a file to a Dataverse file column. - - :param table: Schema name of the table (e.g. ``"account"`` or - ``"new_MyTestTable"``). - :type table: :class:`str` - :param record_id: GUID of the target record. - :type record_id: :class:`str` - :param file_column: Schema name of the file column attribute (e.g., - ``"new_Document"``). If the column doesn't exist, it will be - created automatically. - :type file_column: :class:`str` - :param path: Local filesystem path to the file. The stored filename - will be the basename of this path. - :type path: :class:`str` - :param mode: Upload strategy: ``"auto"`` (default), ``"small"``, or - ``"chunk"``. Auto mode selects small or chunked upload based on - file size. - :type mode: :class:`str` or None - :param mime_type: Explicit MIME type to store with the file (e.g. - ``"application/pdf"``). If not provided, defaults to - ``"application/octet-stream"``. - :type mime_type: :class:`str` or None - :param if_none_match: When True (default), sends - ``If-None-Match: null`` header to only succeed if the column is - currently empty. Set False to always overwrite using - ``If-Match: *``. - :type if_none_match: :class:`bool` - - :raises ~PowerPlatform.Dataverse.core.errors.HttpError: - If the upload fails or the file column is not empty when - ``if_none_match=True``. - :raises FileNotFoundError: If the specified file path does not exist. - - .. rubric:: Example - - Upload a PDF file:: - - client.files.upload( - "account", - account_id, - "new_Contract", - "/path/to/contract.pdf", - mime_type="application/pdf", - ) - - Upload with auto mode selection:: - - client.files.upload( - "email", - email_id, - "new_Attachment", - "/path/to/large_file.zip", - ) - - - diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/operations/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/operations/index.rst deleted file mode 100644 index cbb14381..00000000 --- a/docs_local/autoapi/PowerPlatform/Dataverse/operations/index.rst +++ /dev/null @@ -1,28 +0,0 @@ -PowerPlatform.Dataverse.operations -================================== - -.. py:module:: PowerPlatform.Dataverse.operations - -.. autoapi-nested-parse:: - - Operation namespaces for the Dataverse SDK. - - This module contains the operation namespace classes that organize - SDK operations into logical groups: records, query, and tables. - - - -Submodules ----------- - -.. toctree:: - :maxdepth: 1 - - /autoapi/PowerPlatform/Dataverse/operations/batch/index - /autoapi/PowerPlatform/Dataverse/operations/dataframe/index - /autoapi/PowerPlatform/Dataverse/operations/files/index - /autoapi/PowerPlatform/Dataverse/operations/query/index - /autoapi/PowerPlatform/Dataverse/operations/records/index - /autoapi/PowerPlatform/Dataverse/operations/tables/index - - diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/operations/query/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/operations/query/index.rst deleted file mode 100644 index 740ab5b4..00000000 --- a/docs_local/autoapi/PowerPlatform/Dataverse/operations/query/index.rst +++ /dev/null @@ -1,345 +0,0 @@ -PowerPlatform.Dataverse.operations.query -======================================== - -.. py:module:: PowerPlatform.Dataverse.operations.query - -.. autoapi-nested-parse:: - - Query operations namespace for the Dataverse SDK. - - - -Classes -------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.operations.query.QueryOperations - - -Module Contents ---------------- - -.. py:class:: QueryOperations(client: PowerPlatform.Dataverse.client.DataverseClient) - - Namespace for query operations. - - Accessed via ``client.query``. Provides query and search operations - against Dataverse tables. - - :param client: The parent :class:`~PowerPlatform.Dataverse.client.DataverseClient` instance. - :type client: ~PowerPlatform.Dataverse.client.DataverseClient - - Example:: - - from PowerPlatform.Dataverse.models.filters import col - - client = DataverseClient(base_url, credential) - - # Fluent query builder (recommended) - for record in (client.query.builder("account") - .select("name", "revenue") - .where(col("statecode") == 0) - .order_by("revenue", descending=True) - .top(100) - .execute()): - print(record["name"]) - - # SQL query - rows = client.query.sql("SELECT TOP 10 name FROM account ORDER BY name") - for row in rows: - print(row["name"]) - - - .. py:method:: builder(table: str) -> PowerPlatform.Dataverse.models.query_builder.QueryBuilder - - Create a fluent query builder for the specified table. - - Returns a :class:`~PowerPlatform.Dataverse.models.query_builder.QueryBuilder` - that can be chained with filter, select, and order methods, then - executed directly via ``.execute()``. - - :param table: Table schema name (e.g. ``"account"``). - :type table: :class:`str` - :return: A QueryBuilder instance bound to this client. - :rtype: ~PowerPlatform.Dataverse.models.query_builder.QueryBuilder - - .. rubric:: Example - - Build and execute a query fluently:: - - from PowerPlatform.Dataverse.models.filters import col - - for record in (client.query.builder("account") - .select("name", "revenue") - .where(col("statecode") == 0) - .where(col("revenue") > 1_000_000) - .order_by("revenue", descending=True) - .top(100) - .page_size(50) - .execute()): - print(record["name"]) - - With composable expression tree:: - - from PowerPlatform.Dataverse.models.filters import col - - for record in (client.query.builder("account") - .where((col("statecode") == 0) | (col("statecode") == 1)) - .where(col("revenue") > 100_000) - .execute()): - print(record["name"]) - - - - .. py:method:: sql(sql: str) -> List[PowerPlatform.Dataverse.models.record.Record] - - Execute a read-only SQL query using the Dataverse Web API. - - The Dataverse SQL endpoint supports a broad subset of T-SQL:: - - SELECT / SELECT DISTINCT / SELECT TOP N (0-5000) - FROM table [alias] - INNER JOIN / LEFT JOIN (multi-table, no depth limit) - WHERE (=, !=, >, <, >=, <=, LIKE, IN, NOT IN, IS NULL, - IS NOT NULL, BETWEEN, AND, OR, nested parentheses) - GROUP BY column - ORDER BY column [ASC|DESC] - OFFSET n ROWS FETCH NEXT m ROWS ONLY - COUNT(*), SUM(), AVG(), MIN(), MAX() - - ``SELECT *`` is not supported -- specify column names explicitly. - Use :meth:`sql_columns` to discover available column names for a table. - - Not supported: SELECT *, subqueries, CTE, HAVING, UNION, - RIGHT/FULL/CROSS JOIN, CASE, COALESCE, window functions, - string/date/math functions, INSERT/UPDATE/DELETE. For writes, use - ``client.records`` methods. - - :param sql: Supported SQL SELECT statement. - :type sql: :class:`str` - - :return: List of :class:`~PowerPlatform.Dataverse.models.record.Record` - objects. Returns an empty list when no rows match. - :rtype: list[~PowerPlatform.Dataverse.models.record.Record] - - :raises ~PowerPlatform.Dataverse.core.errors.ValidationError: - If ``sql`` is not a string or is empty. - - .. rubric:: Example - - Basic query:: - - rows = client.query.sql( - "SELECT TOP 10 name FROM account ORDER BY name" - ) - - JOIN with aggregation:: - - rows = client.query.sql( - "SELECT a.name, COUNT(c.contactid) as cnt " - "FROM account a " - "JOIN contact c ON a.accountid = c.parentcustomerid " - "GROUP BY a.name" - ) - - - - .. py:method:: fetchxml(xml: str) -> PowerPlatform.Dataverse.models.fetchxml_query.FetchXmlQuery - - Return an inert :class:`~PowerPlatform.Dataverse.models.fetchxml_query.FetchXmlQuery` object. - - No HTTP request is made until - :meth:`~PowerPlatform.Dataverse.models.fetchxml_query.FetchXmlQuery.execute` - or - :meth:`~PowerPlatform.Dataverse.models.fetchxml_query.FetchXmlQuery.execute_pages` - is called on the returned object. - - Use for SQL-JOIN scenarios, aggregate queries, or other operations that - the OData builder endpoint cannot express. - - :param xml: Well-formed FetchXML query string. The root ```` - element determines the entity set endpoint. - :type xml: :class:`str` - :return: Inert query object with ``.execute()`` and ``.execute_pages()`` methods. - :rtype: :class:`~PowerPlatform.Dataverse.models.fetchxml_query.FetchXmlQuery` - :raises ValueError: If the FetchXML is missing a root ```` element - or the entity ``name`` attribute. - - Example:: - - query = client.query.fetchxml(""" - - - - - - - - - """) - - # Eager — collect all pages: - result = query.execute() - df = result.to_dataframe() - - # Lazy — process one page at a time: - for page in query.execute_pages(): - process(page.to_dataframe()) - - - - .. py:method:: sql_columns(table: str, *, include_system: bool = False) -> List[Dict[str, Any]] - - Return a simplified list of SQL-usable columns for a table. - - Each dict contains ``name`` (logical name for SQL), ``type`` - (Dataverse attribute type), ``is_pk`` (primary key flag), and - ``label`` (display name). Virtual columns are always excluded - because the SQL endpoint cannot query them. - - :param table: Schema name of the table (e.g. ``"account"``). - :type table: :class:`str` - :param include_system: When ``False`` (default), columns that end - with common system suffixes (``_base``, ``versionnumber``, - ``timezoneruleversionnumber``, ``utcconversiontimezonecode``, - ``importsequencenumber``, ``overriddencreatedon``) are excluded. - :type include_system: :class:`bool` - - :return: List of column metadata dicts. - :rtype: list[dict[str, typing.Any]] - - Example:: - - cols = client.query.sql_columns("account") - for c in cols: - print(f"{c['name']:30s} {c['type']:20s} PK={c['is_pk']}") - - - - .. py:method:: odata_select(table: str, *, include_system: bool = False) -> List[str] - - Return a list of column logical names suitable for ``$select``. - - Can be passed directly to ``client.records.get(table, select=...)``. - - :param table: Schema name of the table (e.g. ``"account"``). - :type table: :class:`str` - :param include_system: Include system columns (default ``False``). - :type include_system: :class:`bool` - - :return: List of lowercase column logical names. - :rtype: list[str] - - Example:: - - cols = client.query.odata_select("account") - for page in client.records.get("account", select=cols, top=10): - for r in page: - print(r) - - - - .. py:method:: odata_expands(table: str) -> List[Dict[str, Any]] - - Discover all ``$expand`` navigation properties from a table. - - Returns entries for each outgoing lookup (single-valued navigation - property). Each entry contains the exact PascalCase navigation - property name needed for ``$expand`` and ``@odata.bind``, plus - the target entity set name. - - :param table: Schema name of the table (e.g. ``"contact"``). - :type table: :class:`str` - - :return: List of dicts, each with: - - - ``nav_property`` -- PascalCase navigation property for $expand - - ``target_table`` -- target entity logical name - - ``target_entity_set`` -- target entity set (for @odata.bind) - - ``lookup_attribute`` -- the lookup column logical name - - ``relationship`` -- relationship schema name - - :rtype: list[dict[str, typing.Any]] - - Example:: - - expands = client.query.odata_expands("contact") - for e in expands: - print(f"expand={e['nav_property']} -> {e['target_table']}") - - # Use in a query - e = next(e for e in expands if e['target_table'] == 'account') - for page in client.records.get("contact", - select=["fullname"], - expand=[e['nav_property']]): - ... - - - - .. py:method:: odata_expand(from_table: str, to_table: str) -> str - - Return the navigation property name to ``$expand`` from one table to another. - - Discovers via relationship metadata. Returns the exact PascalCase - string for the ``expand=`` parameter. - - :param from_table: Schema name of the source table (e.g. ``"contact"``). - :type from_table: :class:`str` - :param to_table: Schema name of the target table (e.g. ``"account"``). - :type to_table: :class:`str` - - :return: The navigation property name (PascalCase). - :rtype: :class:`str` - - :raises ValueError: If no navigation property found for the target. - - Example:: - - nav = client.query.odata_expand("contact", "account") - # Returns e.g. "parentcustomerid_account" - for page in client.records.get("contact", - select=["fullname"], - expand=[nav], - top=5): - for r in page: - acct = r.get(nav) or {} - print(f"{r['fullname']} -> {acct.get('name', 'N/A')}") - - - - .. py:method:: odata_bind(from_table: str, to_table: str, target_id: str) -> Dict[str, str] - - Build an ``@odata.bind`` entry for setting a lookup field. - - Auto-discovers the navigation property name and entity set name - from metadata. Returns a single-entry dict that can be merged - into a create or update payload. - - :param from_table: Schema name of the entity being created/updated. - :type from_table: :class:`str` - :param to_table: Schema name of the target entity the lookup points to. - :type to_table: :class:`str` - :param target_id: GUID of the target record. - :type target_id: :class:`str` - - :return: A dict like ``{"NavProp@odata.bind": "/entityset(guid)"}``. - :rtype: dict[str, str] - - :raises ValueError: If no relationship found between the tables. - - Example:: - - # Instead of manually constructing: - # {"parentcustomerid_account@odata.bind": "/accounts(guid)"} - # Just do: - bind = client.query.odata_bind("contact", "account", acct_id) - client.records.create("contact", { - "firstname": "Jane", - "lastname": "Doe", - **bind, - }) - - - diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/operations/records/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/operations/records/index.rst deleted file mode 100644 index b76711dd..00000000 --- a/docs_local/autoapi/PowerPlatform/Dataverse/operations/records/index.rst +++ /dev/null @@ -1,461 +0,0 @@ -PowerPlatform.Dataverse.operations.records -========================================== - -.. py:module:: PowerPlatform.Dataverse.operations.records - -.. autoapi-nested-parse:: - - Record CRUD operations namespace for the Dataverse SDK. - - - -Classes -------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.operations.records.RecordOperations - - -Module Contents ---------------- - -.. py:class:: RecordOperations(client: PowerPlatform.Dataverse.client.DataverseClient) - - Namespace for record-level CRUD operations. - - Accessed via ``client.records``. Provides create, update, delete, and get - operations on individual Dataverse records. - - :param client: The parent :class:`~PowerPlatform.Dataverse.client.DataverseClient` instance. - :type client: ~PowerPlatform.Dataverse.client.DataverseClient - - Example:: - - client = DataverseClient(base_url, credential) - - # Create a single record - guid = client.records.create("account", {"name": "Contoso Ltd"}) - - # Get a record - record = client.records.get("account", guid, select=["name"]) - - # Update a record - client.records.update("account", guid, {"telephone1": "555-0100"}) - - # Delete a record - client.records.delete("account", guid) - - - .. py:method:: create(table: str, data: Dict[str, Any]) -> str - create(table: str, data: List[Dict[str, Any]]) -> List[str] - - Create one or more records in a Dataverse table. - - When ``data`` is a single dictionary, creates one record and returns its - GUID as a string. When ``data`` is a list of dictionaries, creates all - records via the ``CreateMultiple`` action and returns a list of GUIDs. - - :param table: Schema name of the table (e.g. ``"account"`` or ``"new_MyTestTable"``). - :type table: :class:`str` - :param data: A single record dictionary or a list of record dictionaries. - Each dictionary maps column schema names to values. - :type data: dict or list[dict] - - :return: A single GUID string for a single record, or a list of GUID - strings for bulk creation. - :rtype: str or list[str] - - :raises TypeError: If ``data`` is not a dict or list[dict]. - - .. rubric:: Example - - Create a single record:: - - guid = client.records.create("account", {"name": "Contoso"}) - print(f"Created: {guid}") - - Create multiple records:: - - guids = client.records.create("account", [ - {"name": "Contoso"}, - {"name": "Fabrikam"}, - ]) - print(f"Created {len(guids)} accounts") - - - - .. py:method:: update(table: str, ids: Union[str, List[str]], changes: Union[Dict[str, Any], List[Dict[str, Any]]]) -> None - - Update one or more records in a Dataverse table. - - Supports three usage patterns: - - 1. **Single** -- ``update("account", "guid", {"name": "New"})`` - 2. **Broadcast** -- ``update("account", [id1, id2], {"status": 1})`` - applies the same changes dict to every ID. - 3. **Paired** -- ``update("account", [id1, id2], [ch1, ch2])`` - applies each changes dict to its corresponding ID (lists must be - equal length). - - :param table: Schema name of the table (e.g. ``"account"``). - :type table: :class:`str` - :param ids: A single GUID string, or a list of GUID strings. - :type ids: str or list[str] - :param changes: A dictionary of field changes (single/broadcast), or a - list of dictionaries (paired, one per ID). - :type changes: dict or list[dict] - - :raises TypeError: If ``ids`` is not str or list[str], or if ``changes`` - does not match the expected pattern. - - .. rubric:: Example - - Single update:: - - client.records.update("account", account_id, {"telephone1": "555-0100"}) - - Broadcast update:: - - client.records.update("account", [id1, id2], {"statecode": 1}) - - Paired update:: - - client.records.update( - "account", - [id1, id2], - [{"name": "Name A"}, {"name": "Name B"}], - ) - - - - .. py:method:: delete(table: str, ids: str) -> None - delete(table: str, ids: List[str], *, use_bulk_delete: bool = True) -> Optional[str] - - Delete one or more records from a Dataverse table. - - When ``ids`` is a single string, deletes that one record. When ``ids`` - is a list, either executes a BulkDelete action (returning the async job - ID) or deletes each record sequentially depending on ``use_bulk_delete``. - - :param table: Schema name of the table (e.g. ``"account"``). - :type table: :class:`str` - :param ids: A single GUID string, or a list of GUID strings. - :type ids: str or list[str] - :param use_bulk_delete: When True (default) and ``ids`` is a list, use - the BulkDelete action and return its async job ID. When False, delete - records one at a time. - :type use_bulk_delete: :class:`bool` - - :return: The BulkDelete job ID when bulk-deleting; otherwise None. - :rtype: :class:`str` or None - - :raises TypeError: If ``ids`` is not str or list[str]. - - .. rubric:: Example - - Delete a single record:: - - client.records.delete("account", account_id) - - Bulk delete:: - - job_id = client.records.delete("account", [id1, id2, id3]) - - - - .. py:method:: get(table: str, record_id: str, *, select: Optional[List[str]] = None) -> PowerPlatform.Dataverse.models.record.Record - get(table: str, *, select: Optional[List[str]] = None, filter: Optional[str] = None, orderby: Optional[List[str]] = None, top: Optional[int] = None, expand: Optional[List[str]] = None, page_size: Optional[int] = None, count: bool = False, include_annotations: Optional[str] = None) -> Iterable[List[PowerPlatform.Dataverse.models.record.Record]] - - Fetch a single record by ID, or fetch multiple records with pagination. - - This method has two usage patterns: - - **Fetch a single record** -- ``get(table, record_id, *, select=...)`` - - Pass ``record_id`` as a positional argument to retrieve one record - and get back a :class:`dict`. Query parameters (``filter``, - ``orderby``, ``top``, ``expand``, ``page_size``) must not be provided. - - **Fetch multiple records** -- ``get(table, *, select=..., filter=..., ...)`` - - Omit ``record_id`` to perform a paginated fetch and get back a - generator that yields one page (list of record dicts) at a time. - Automatically follows ``@odata.nextLink`` for server-side paging. - - :param table: Schema name of the table (e.g. ``"account"`` or - ``"new_MyTestTable"``). - :type table: :class:`str` - :param record_id: GUID of the record to retrieve. When omitted, - performs a multi-record fetch instead. - :type record_id: :class:`str` or None - :param select: Optional list of column logical names to include. - Column names are automatically lowercased. - :type select: list[str] or None - :param filter: Optional OData ``$filter`` expression (e.g. - ``"name eq 'Contoso'"``). Column names in filter expressions must - use exact lowercase logical names. Only used for multi-record - queries. - :type filter: :class:`str` or None - :param orderby: Optional list of sort expressions (e.g. - ``["name asc", "createdon desc"]``). Column names are - automatically lowercased. Only used for multi-record queries. - :type orderby: list[str] or None - :param top: Optional maximum total number of records to return. Only - used for multi-record queries. - :type top: :class:`int` or None - :param expand: Optional list of navigation properties to expand (e.g. - ``["primarycontactid"]``). Case-sensitive; must match - server-defined names exactly. Only used for multi-record queries. - :type expand: list[str] or None - :param page_size: Optional per-page size hint sent via - ``Prefer: odata.maxpagesize``. Only used for multi-record queries. - :type page_size: :class:`int` or None - :param count: If ``True``, adds ``$count=true`` to include a total - record count in the response. Only used for multi-record queries. - :type count: :class:`bool` - :param include_annotations: OData annotation pattern for the - ``Prefer: odata.include-annotations`` header (e.g. ``"*"`` or - ``"OData.Community.Display.V1.FormattedValue"``), or ``None``. - Only used for multi-record queries. - :type include_annotations: :class:`str` or None - - :return: A single :class:`~PowerPlatform.Dataverse.models.record.Record` - when ``record_id`` is provided, or a generator yielding pages - (lists of :class:`~PowerPlatform.Dataverse.models.record.Record`) - when fetching multiple records. - :rtype: ~PowerPlatform.Dataverse.models.record.Record or - collections.abc.Iterable[list[~PowerPlatform.Dataverse.models.record.Record]] - - :raises TypeError: If ``record_id`` is provided but not a string. - :raises ValueError: If query parameters are provided alongside - ``record_id``. - - .. rubric:: Example - - Fetch a single record:: - - record = client.records.get( - "account", account_id, select=["name", "telephone1"] - ) - print(record["name"]) - - Fetch multiple records with pagination:: - - for page in client.records.get( - "account", - filter="statecode eq 0", - select=["name", "telephone1"], - page_size=50, - ): - for record in page: - print(record["name"]) - - - - .. py:method:: retrieve(table: str, record_id: str, *, select: Optional[List[str]] = None, expand: Optional[List[str]] = None, include_annotations: Optional[str] = None) -> Optional[PowerPlatform.Dataverse.models.record.Record] - - Fetch a single record by its GUID, returning ``None`` if not found. - - GA replacement for ``records.get(table, record_id)``. Returns ``None`` - instead of raising when the record does not exist (HTTP 404). - - :param table: Schema name of the table (e.g. ``"account"``). - :type table: :class:`str` - :param record_id: GUID of the record to retrieve. - :type record_id: :class:`str` - :param select: Optional list of column logical names to include. - :type select: list[str] or None - :param expand: Optional list of navigation properties to expand (e.g. - ``["primarycontactid"]``). Navigation property names are - case-sensitive and must match the entity's ``$metadata``. - :type expand: list[str] or None - :param include_annotations: OData annotation pattern for the - ``Prefer: odata.include-annotations`` header (e.g. ``"*"`` or - ``"OData.Community.Display.V1.FormattedValue"``), or ``None``. - :type include_annotations: :class:`str` or None - :return: Typed record, or ``None`` if not found. - :rtype: :class:`~PowerPlatform.Dataverse.models.record.Record` or None - - Example:: - - record = client.records.retrieve( - "account", account_id, - select=["name", "statuscode"], - expand=["primarycontactid"], - include_annotations="OData.Community.Display.V1.FormattedValue", - ) - if record is not None: - contact = record.get("primarycontactid") or {} - print(contact.get("fullname")) - - - - .. py:method:: list(table: str, *, filter: Optional[Union[str, PowerPlatform.Dataverse.models.filters.FilterExpression]] = None, select: Optional[List[str]] = None, orderby: Optional[List[str]] = None, top: Optional[int] = None, expand: Optional[List[str]] = None, page_size: Optional[int] = None, count: bool = False, include_annotations: Optional[str] = None) -> PowerPlatform.Dataverse.models.record.QueryResult - - Fetch multiple records and return them as a :class:`~PowerPlatform.Dataverse.models.record.QueryResult`. - - GA replacement for ``records.get(table, filter=...)``. All pages are - collected eagerly and returned as a single :class:`~PowerPlatform.Dataverse.models.record.QueryResult`. - - :param table: Schema name of the table (e.g. ``"account"``). - :type table: :class:`str` - :param filter: Optional OData filter string or :class:`~PowerPlatform.Dataverse.models.filters.FilterExpression`. - :type filter: str or FilterExpression or None - :param select: Optional list of column logical names to include. - :type select: list[str] or None - :param orderby: Optional list of sort expressions (e.g. ``["name asc", "createdon desc"]``). - :type orderby: list[str] or None - :param top: Maximum total number of records to return. - :type top: int or None - :param expand: Optional list of navigation properties to expand. - :type expand: list[str] or None - :param page_size: Per-page size hint via ``Prefer: odata.maxpagesize``. - :type page_size: int or None - :param count: If ``True``, adds ``$count=true`` to include a total record count. - :type count: bool - :param include_annotations: OData annotation pattern for the - ``Prefer: odata.include-annotations`` header, or ``None``. - :type include_annotations: :class:`str` or None - :return: All matching records collected into a :class:`~PowerPlatform.Dataverse.models.record.QueryResult`. - :rtype: :class:`~PowerPlatform.Dataverse.models.record.QueryResult` - - Example:: - - from PowerPlatform.Dataverse import col - - result = client.records.list( - "account", - filter=col("statecode") == 0, - select=["name", "statuscode"], - orderby=["name asc"], - top=100, - include_annotations="OData.Community.Display.V1.FormattedValue", - ) - for record in result: - print(record["name"], record.get("statuscode@OData.Community.Display.V1.FormattedValue")) - - - - .. py:method:: list_pages(table: str, *, filter: Optional[Union[str, PowerPlatform.Dataverse.models.filters.FilterExpression]] = None, select: Optional[List[str]] = None, orderby: Optional[List[str]] = None, top: Optional[int] = None, expand: Optional[List[str]] = None, page_size: Optional[int] = None, count: bool = False, include_annotations: Optional[str] = None) -> Iterator[PowerPlatform.Dataverse.models.record.QueryResult] - - Lazily yield one :class:`~PowerPlatform.Dataverse.models.record.QueryResult` per HTTP page. - - Streaming counterpart to :meth:`list`. Each iteration triggers one - network request via ``@odata.nextLink``. One-shot — do not iterate - more than once. - - :param table: Schema name of the table (e.g. ``"account"``). - :type table: :class:`str` - :param filter: Optional OData filter string or :class:`~PowerPlatform.Dataverse.models.filters.FilterExpression`. - :type filter: str or FilterExpression or None - :param select: Optional list of column logical names to include. - :type select: list[str] or None - :param orderby: Optional list of sort expressions (e.g. ``["name asc", "createdon desc"]``). - :type orderby: list[str] or None - :param top: Maximum total number of records to return. - :type top: int or None - :param expand: Optional list of navigation properties to expand. - :type expand: list[str] or None - :param page_size: Per-page size hint via ``Prefer: odata.maxpagesize``. - :type page_size: int or None - :param count: If ``True``, adds ``$count=true`` to include a total record count. - :type count: bool - :param include_annotations: OData annotation pattern for the - ``Prefer: odata.include-annotations`` header, or ``None``. - :type include_annotations: :class:`str` or None - :return: Iterator of per-page :class:`~PowerPlatform.Dataverse.models.record.QueryResult` objects. - :rtype: Iterator[:class:`~PowerPlatform.Dataverse.models.record.QueryResult`] - - Example:: - - for page in client.records.list_pages( - "account", - filter="statecode eq 0", - orderby=["name asc"], - page_size=200, - ): - process(page.to_dataframe()) - - - - .. py:method:: upsert(table: str, items: List[Union[PowerPlatform.Dataverse.models.upsert.UpsertItem, Dict[str, Any]]]) -> None - - Upsert one or more records identified by alternate keys. - - When ``items`` contains a single entry, performs a single upsert via PATCH - using the alternate key in the URL. When ``items`` contains multiple entries, - uses the ``UpsertMultiple`` bulk action. - - Each item must be either a :class:`~PowerPlatform.Dataverse.models.upsert.UpsertItem` - or a plain ``dict`` with ``"alternate_key"`` and ``"record"`` keys (both dicts). - - :param table: Schema name of the table (e.g. ``"account"`` or ``"new_MyTestTable"``). - :type table: str - :param items: Non-empty list of :class:`~PowerPlatform.Dataverse.models.upsert.UpsertItem` - instances or dicts with ``"alternate_key"`` and ``"record"`` keys. - :type items: list[UpsertItem | dict] - - :return: ``None`` - :rtype: None - - :raises TypeError: If ``items`` is not a non-empty list, or if any element is - neither a :class:`~PowerPlatform.Dataverse.models.upsert.UpsertItem` nor a - dict with ``"alternate_key"`` and ``"record"`` keys. - - .. rubric:: Example - - Upsert a single record using ``UpsertItem``:: - - from PowerPlatform.Dataverse.models import UpsertItem - - client.records.upsert("account", [ - UpsertItem( - alternate_key={"accountnumber": "ACC-001"}, - record={"name": "Contoso Ltd", "description": "Primary account"}, - ) - ]) - - Upsert a single record using a plain dict:: - - client.records.upsert("account", [ - { - "alternate_key": {"accountnumber": "ACC-001"}, - "record": {"name": "Contoso Ltd", "description": "Primary account"}, - }, - ]) - - Upsert multiple records using ``UpsertItem``:: - - from PowerPlatform.Dataverse.models import UpsertItem - - client.records.upsert("account", [ - UpsertItem( - alternate_key={"accountnumber": "ACC-001"}, - record={"name": "Contoso Ltd", "description": "Primary account"}, - ), - UpsertItem( - alternate_key={"accountnumber": "ACC-002"}, - record={"name": "Fabrikam Inc", "description": "Partner account"}, - ), - ]) - - Upsert multiple records using plain dicts:: - - client.records.upsert("account", [ - { - "alternate_key": {"accountnumber": "ACC-001"}, - "record": {"name": "Contoso Ltd", "description": "Primary account"}, - }, - { - "alternate_key": {"accountnumber": "ACC-002"}, - "record": {"name": "Fabrikam Inc", "description": "Partner account"}, - }, - ]) - - The ``alternate_key`` dict may contain multiple columns when the configured - alternate key is composite, e.g. - ``{"accountnumber": "ACC-001", "address1_postalcode": "98052"}``. - - - diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/operations/tables/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/operations/tables/index.rst deleted file mode 100644 index 0cf63a2e..00000000 --- a/docs_local/autoapi/PowerPlatform/Dataverse/operations/tables/index.rst +++ /dev/null @@ -1,683 +0,0 @@ -PowerPlatform.Dataverse.operations.tables -========================================= - -.. py:module:: PowerPlatform.Dataverse.operations.tables - -.. autoapi-nested-parse:: - - Table metadata operations namespace for the Dataverse SDK. - - - -Classes -------- - -.. autoapisummary:: - - PowerPlatform.Dataverse.operations.tables.TableOperations - - -Module Contents ---------------- - -.. py:class:: TableOperations(client: PowerPlatform.Dataverse.client.DataverseClient) - - Namespace for table-level metadata operations. - - Accessed via ``client.tables``. Provides operations to create, delete, - inspect, and list Dataverse tables, as well as add and remove columns. - - :param client: The parent :class:`~PowerPlatform.Dataverse.client.DataverseClient` instance. - :type client: ~PowerPlatform.Dataverse.client.DataverseClient - - Example:: - - client = DataverseClient(base_url, credential) - - # Create a table - info = client.tables.create( - "new_Product", - {"new_Price": "decimal", "new_InStock": "bool"}, - solution="MySolution", - ) - - # List tables - tables = client.tables.list() - - # Get table info - info = client.tables.get("new_Product") - - # Add columns - client.tables.add_columns("new_Product", {"new_Rating": "int"}) - - # Remove columns - client.tables.remove_columns("new_Product", "new_Rating") - - # Delete a table - client.tables.delete("new_Product") - - - .. py:method:: create(table: str, columns: Dict[str, Any], *, solution: Optional[str] = None, primary_column: Optional[str] = None, display_name: Optional[str] = None) -> PowerPlatform.Dataverse.models.table_info.TableInfo - - Create a custom table with the specified columns. - - :param table: Schema name of the table with customization prefix - (e.g. ``"new_MyTestTable"``). - :type table: :class:`str` - :param columns: Mapping of column schema names (with customization - prefix) to their types. Supported types include ``"string"`` - (or ``"text"``), ``"memo"`` (or ``"multiline"``), - ``"int"`` (or ``"integer"``), ``"decimal"`` - (or ``"money"``), ``"float"`` (or ``"double"``), ``"datetime"`` - (or ``"date"``), ``"bool"`` (or ``"boolean"``), ``"file"``, and - ``Enum`` subclasses - (for local option sets). - :type columns: :class:`dict` - :param solution: Optional solution unique name that should own the new - table. When omitted the table is created in the default solution. - :type solution: :class:`str` or None - :param primary_column: Optional primary name column schema name with - customization prefix (e.g. ``"new_ProductName"``). If not provided, - defaults to ``"{prefix}_Name"``. - :type primary_column: :class:`str` or None - :param display_name: Human-readable display name for the table - (e.g. ``"Product"``). When omitted, defaults to the table schema name. - :type display_name: :class:`str` or None - - :return: Table metadata with ``schema_name``, ``entity_set_name``, - ``logical_name``, ``metadata_id``, and ``columns_created``. - Supports dict-like access with legacy keys for backward - compatibility. - :rtype: :class:`~PowerPlatform.Dataverse.models.table_info.TableInfo` - - :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: - If table creation fails or the table already exists. - - .. rubric:: Example - - Create a table with simple columns:: - - from enum import IntEnum - - class ItemStatus(IntEnum): - ACTIVE = 1 - INACTIVE = 2 - - result = client.tables.create( - "new_Product", - { - "new_Title": "string", - "new_Price": "decimal", - "new_Status": ItemStatus, - }, - solution="MySolution", - primary_column="new_ProductName", - display_name="Product", - ) - print(f"Created: {result['table_schema_name']}") - - - - .. py:method:: delete(table: str) -> None - - Delete a custom table by schema name. - - :param table: Schema name of the table (e.g. ``"new_MyTestTable"``). - :type table: :class:`str` - - :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: - If the table does not exist or deletion fails. - - .. warning:: - This operation is irreversible and will delete all records in the - table along with the table definition. - - Example:: - - client.tables.delete("new_MyTestTable") - - - - .. py:method:: get(table: str) -> Optional[PowerPlatform.Dataverse.models.table_info.TableInfo] - - Get basic metadata for a table if it exists. - - :param table: Schema name of the table (e.g. ``"new_MyTestTable"`` - or ``"account"``). - :type table: :class:`str` - - :return: Table metadata, or ``None`` if the table is not found. - Supports dict-like access with legacy keys for backward - compatibility. - :rtype: :class:`~PowerPlatform.Dataverse.models.table_info.TableInfo` - or None - - Example:: - - info = client.tables.get("new_MyTestTable") - if info: - print(f"Logical name: {info['table_logical_name']}") - print(f"Entity set: {info['entity_set_name']}") - - - - .. py:method:: list(*, filter: Optional[str] = None, select: Optional[List[str]] = None) -> List[Dict[str, Any]] - - List all non-private tables in the Dataverse environment. - - By default returns every table where ``IsPrivate eq false``. Supply - an optional OData ``$filter`` expression to further narrow the results. - The expression is combined with the default ``IsPrivate eq false`` - clause using ``and``. - - :param filter: Optional OData ``$filter`` expression to further narrow - the list of returned tables (e.g. - ``"SchemaName eq 'Account'"``). Column names in filter - expressions must use the exact property names from the - ``EntityDefinitions`` metadata (typically PascalCase). - :type filter: :class:`str` or None - :param select: Optional list of property names to include in the - response (projected via the OData ``$select`` query option). - Property names must use the exact PascalCase names from the - ``EntityDefinitions`` metadata (e.g. - ``["LogicalName", "SchemaName", "DisplayName"]``). - When ``None`` (the default) or an empty list, all properties are - returned. - :type select: list[str] or None - - :return: List of EntityDefinition metadata dictionaries. - :rtype: list[dict] - - Example:: - - # List all non-private tables - tables = client.tables.list() - for table in tables: - print(table["LogicalName"]) - - # List only tables whose schema name starts with "new_" - custom_tables = client.tables.list( - filter="startswith(SchemaName, 'new_')" - ) - - # List tables with only specific properties - tables = client.tables.list( - select=["LogicalName", "SchemaName", "EntitySetName"] - ) - - - - .. py:method:: add_columns(table: str, columns: Dict[str, Any]) -> List[str] - - Add one or more columns to an existing table. - - :param table: Schema name of the table (e.g. ``"new_MyTestTable"``). - :type table: :class:`str` - :param columns: Mapping of column schema names (with customization - prefix) to their types. Supported types are the same as for - :meth:`create`. - :type columns: :class:`dict` - - :return: Schema names of the columns that were created. - :rtype: list[str] - - :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: - If the table does not exist. - - Example:: - - created = client.tables.add_columns( - "new_MyTestTable", - {"new_Notes": "string", "new_Active": "bool"}, - ) - print(created) # ['new_Notes', 'new_Active'] - - - - .. py:method:: remove_columns(table: str, columns: Union[str, List[str]]) -> List[str] - - Remove one or more columns from a table. - - :param table: Schema name of the table (e.g. ``"new_MyTestTable"``). - :type table: :class:`str` - :param columns: Column schema name or list of column schema names to - remove. Must include the customization prefix (e.g. - ``"new_TestColumn"``). - :type columns: str or list[str] - - :return: Schema names of the columns that were removed. - :rtype: list[str] - - :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: - If the table or a specified column does not exist. - - Example:: - - removed = client.tables.remove_columns( - "new_MyTestTable", - ["new_Notes", "new_Active"], - ) - print(removed) # ['new_Notes', 'new_Active'] - - - - .. py:method:: create_one_to_many_relationship(lookup: PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata, relationship: PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata, *, solution: Optional[str] = None) -> PowerPlatform.Dataverse.models.relationship.RelationshipInfo - - Create a one-to-many relationship between tables. - - This operation creates both the relationship and the lookup attribute - on the referencing table. - - :param lookup: Metadata defining the lookup attribute. - :type lookup: ~PowerPlatform.Dataverse.models.relationship.LookupAttributeMetadata - :param relationship: Metadata defining the relationship. - :type relationship: ~PowerPlatform.Dataverse.models.relationship.OneToManyRelationshipMetadata - :param solution: Optional solution unique name to add relationship to. - :type solution: :class:`str` or None - - :return: Relationship metadata with ``relationship_id``, - ``relationship_schema_name``, ``relationship_type``, - ``lookup_schema_name``, ``referenced_entity``, and - ``referencing_entity``. - :rtype: :class:`~PowerPlatform.Dataverse.models.relationship.RelationshipInfo` - - :raises ~PowerPlatform.Dataverse.core.errors.HttpError: - If the Web API request fails. - - .. rubric:: Example - - Create a one-to-many relationship: Department (1) -> Employee (N):: - - from PowerPlatform.Dataverse.models import ( - LookupAttributeMetadata, - OneToManyRelationshipMetadata, - Label, - LocalizedLabel, - CascadeConfiguration, - ) - from PowerPlatform.Dataverse.common.constants import ( - CASCADE_BEHAVIOR_REMOVE_LINK, - ) - - lookup = LookupAttributeMetadata( - schema_name="new_DepartmentId", - display_name=Label( - localized_labels=[ - LocalizedLabel(label="Department", language_code=1033) - ] - ), - ) - - relationship = OneToManyRelationshipMetadata( - schema_name="new_Department_Employee", - referenced_entity="new_department", - referencing_entity="new_employee", - referenced_attribute="new_departmentid", - cascade_configuration=CascadeConfiguration( - delete=CASCADE_BEHAVIOR_REMOVE_LINK, - ), - ) - - result = client.tables.create_one_to_many_relationship(lookup, relationship) - print(f"Created lookup field: {result.lookup_schema_name}") - - - - .. py:method:: create_many_to_many_relationship(relationship: PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata, *, solution: Optional[str] = None) -> PowerPlatform.Dataverse.models.relationship.RelationshipInfo - - Create a many-to-many relationship between tables. - - This operation creates a many-to-many relationship and an intersect - table to manage the relationship. - - :param relationship: Metadata defining the many-to-many relationship. - :type relationship: ~PowerPlatform.Dataverse.models.relationship.ManyToManyRelationshipMetadata - :param solution: Optional solution unique name to add relationship to. - :type solution: :class:`str` or None - - :return: Relationship metadata with ``relationship_id``, - ``relationship_schema_name``, ``relationship_type``, - ``entity1_logical_name``, and ``entity2_logical_name``. - :rtype: :class:`~PowerPlatform.Dataverse.models.relationship.RelationshipInfo` - - :raises ~PowerPlatform.Dataverse.core.errors.HttpError: - If the Web API request fails. - - .. rubric:: Example - - Create a many-to-many relationship: Employee <-> Project:: - - from PowerPlatform.Dataverse.models import ( - ManyToManyRelationshipMetadata, - ) - - relationship = ManyToManyRelationshipMetadata( - schema_name="new_employee_project", - entity1_logical_name="new_employee", - entity2_logical_name="new_project", - ) - - result = client.tables.create_many_to_many_relationship(relationship) - print(f"Created: {result.relationship_schema_name}") - - - - .. py:method:: delete_relationship(relationship_id: str) -> None - - Delete a relationship by its metadata ID. - - :param relationship_id: The GUID of the relationship metadata. - :type relationship_id: :class:`str` - - :raises ~PowerPlatform.Dataverse.core.errors.HttpError: - If the Web API request fails. - - .. warning:: - Deleting a relationship also removes the associated lookup attribute - for one-to-many relationships. This operation is irreversible. - - Example:: - - client.tables.delete_relationship( - "12345678-1234-1234-1234-123456789abc" - ) - - - - .. py:method:: get_relationship(schema_name: str) -> Optional[PowerPlatform.Dataverse.models.relationship.RelationshipInfo] - - Retrieve relationship metadata by schema name. - - :param schema_name: The schema name of the relationship. - :type schema_name: :class:`str` - - :return: Relationship metadata, or ``None`` if not found. - :rtype: :class:`~PowerPlatform.Dataverse.models.relationship.RelationshipInfo` - or None - - :raises ~PowerPlatform.Dataverse.core.errors.HttpError: - If the Web API request fails. - - Example:: - - rel = client.tables.get_relationship("new_Department_Employee") - if rel: - print(f"Found: {rel.relationship_schema_name}") - - - - .. py:method:: create_lookup_field(referencing_table: str, lookup_field_name: str, referenced_table: str, *, display_name: Optional[str] = None, description: Optional[str] = None, required: bool = False, cascade_delete: str = CASCADE_BEHAVIOR_REMOVE_LINK, solution: Optional[str] = None, language_code: int = 1033) -> PowerPlatform.Dataverse.models.relationship.RelationshipInfo - - Create a simple lookup field relationship. - - This is a convenience method that wraps :meth:`create_one_to_many_relationship` - for the common case of adding a lookup field to an existing table. - - :param referencing_table: Logical name of the table that will have - the lookup field (child table). - :type referencing_table: :class:`str` - :param lookup_field_name: Schema name for the lookup field - (e.g., ``"new_AccountId"``). - :type lookup_field_name: :class:`str` - :param referenced_table: Logical name of the table being referenced - (parent table). - :type referenced_table: :class:`str` - :param display_name: Display name for the lookup field. Defaults to - the referenced table name. - :type display_name: :class:`str` or None - :param description: Optional description for the lookup field. - :type description: :class:`str` or None - :param required: Whether the lookup is required. Defaults to ``False``. - :type required: :class:`bool` - :param cascade_delete: Delete behavior (``"RemoveLink"``, - ``"Cascade"``, ``"Restrict"``). Defaults to ``"RemoveLink"``. - :type cascade_delete: :class:`str` - :param solution: Optional solution unique name to add the relationship - to. - :type solution: :class:`str` or None - :param language_code: Language code for labels. Defaults to 1033 - (English). - :type language_code: :class:`int` - - :return: Relationship metadata with ``relationship_id``, - ``relationship_schema_name``, ``relationship_type``, - ``lookup_schema_name``, ``referenced_entity``, and - ``referencing_entity``. - :rtype: :class:`~PowerPlatform.Dataverse.models.relationship.RelationshipInfo` - - :raises ~PowerPlatform.Dataverse.core.errors.HttpError: - If the Web API request fails. - - .. rubric:: Example - - Create a simple lookup field:: - - result = client.tables.create_lookup_field( - referencing_table="new_order", - lookup_field_name="new_AccountId", - referenced_table="account", - display_name="Account", - required=True, - cascade_delete=CASCADE_BEHAVIOR_REMOVE_LINK, - ) - print(f"Created lookup: {result['lookup_schema_name']}") - - - - .. py:method:: create_alternate_key(table: str, key_name: str, columns: List[str], *, display_name: Optional[str] = None, language_code: int = 1033) -> PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo - - Create an alternate key on a table. - - Alternate keys allow upsert operations to identify records by one or - more columns instead of the primary GUID. After creation the key is - queued for index building; its :attr:`~PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo.status` will - transition from ``"Pending"`` to ``"Active"`` once the index is ready. - - :param table: Schema name of the table (e.g. ``"new_Product"``). - :type table: :class:`str` - :param key_name: Schema name for the new alternate key - (e.g. ``"new_product_code_key"``). - :type key_name: :class:`str` - :param columns: List of column logical names that compose the key - (e.g. ``["new_productcode"]``). - :type columns: list[str] - :param display_name: Display name for the key. Defaults to - ``key_name`` if not provided. - :type display_name: :class:`str` or None - :param language_code: Language code for labels. Defaults to 1033 - (English). - :type language_code: :class:`int` - - :return: Metadata for the newly created alternate key. - :rtype: :class:`~PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo` - - :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: - If the table does not exist. - :raises ~PowerPlatform.Dataverse.core.errors.HttpError: - If the Web API request fails. - - .. rubric:: Example - - Create a single-column alternate key for upsert:: - - key = client.tables.create_alternate_key( - "new_Product", - "new_product_code_key", - ["new_productcode"], - display_name="Product Code", - ) - print(f"Key ID: {key.metadata_id}") - print(f"Columns: {key.key_attributes}") - - - - .. py:method:: get_alternate_keys(table: str) -> List[PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo] - - List all alternate keys defined on a table. - - :param table: Schema name of the table (e.g. ``"new_Product"``). - :type table: :class:`str` - - :return: List of alternate key metadata objects. May be empty if no - alternate keys are defined. - :rtype: list[~PowerPlatform.Dataverse.models.table_info.AlternateKeyInfo] - - :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: - If the table does not exist. - :raises ~PowerPlatform.Dataverse.core.errors.HttpError: - If the Web API request fails. - - .. rubric:: Example - - List alternate keys and print their status:: - - keys = client.tables.get_alternate_keys("new_Product") - for key in keys: - print(f"{key.schema_name}: {key.status}") - - - - .. py:method:: delete_alternate_key(table: str, key_id: str) -> None - - Delete an alternate key by its metadata ID. - - :param table: Schema name of the table (e.g. ``"new_Product"``). - :type table: :class:`str` - :param key_id: Metadata GUID of the alternate key to delete. - :type key_id: :class:`str` - - :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: - If the table does not exist. - :raises ~PowerPlatform.Dataverse.core.errors.HttpError: - If the Web API request fails. - - .. warning:: - Deleting an alternate key that is in use by upsert operations will - cause those operations to fail. This operation is irreversible. - - Example:: - - client.tables.delete_alternate_key( - "new_Product", - "12345678-1234-1234-1234-123456789abc", - ) - - - - .. py:method:: list_columns(table: str, *, select: Optional[List[str]] = None, filter: Optional[str] = None) -> List[Dict[str, Any]] - - List all attribute (column) definitions for a table. - - :param table: Schema name of the table (e.g. ``"account"`` or - ``"new_Product"``). - :type table: :class:`str` - :param select: Optional list of property names to project via - ``$select``. Values are passed as-is (PascalCase). - :type select: list[str] or None - :param filter: Optional OData ``$filter`` expression. For example, - ``"AttributeType eq 'String'"`` returns only string columns. - :type filter: :class:`str` or None - - :return: List of raw attribute metadata dictionaries. - :rtype: list[dict[str, typing.Any]] - - :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: - If the table is not found. - :raises ~PowerPlatform.Dataverse.core.errors.HttpError: - If the Web API request fails. - - Example:: - - # List all columns on the account table - columns = client.tables.list_columns("account") - for col in columns: - print(f"{col['LogicalName']} ({col.get('AttributeType')})") - - # List only specific properties - columns = client.tables.list_columns( - "account", - select=["LogicalName", "SchemaName", "AttributeType"], - ) - - # Filter to only string attributes - columns = client.tables.list_columns( - "account", - filter="AttributeType eq 'String'", - ) - - - - .. py:method:: list_relationships(*, filter: Optional[str] = None, select: Optional[List[str]] = None) -> List[Dict[str, Any]] - - List all relationship definitions in the environment. - - :param filter: Optional OData ``$filter`` expression. For example, - ``"RelationshipType eq Microsoft.Dynamics.CRM.RelationshipType'OneToManyRelationship'"`` - returns only one-to-many relationships. - :type filter: :class:`str` or None - :param select: Optional list of property names to project via - ``$select``. Values are passed as-is (PascalCase). - :type select: list[str] or None - - :return: List of raw relationship metadata dictionaries. - :rtype: list[dict[str, typing.Any]] - - :raises ~PowerPlatform.Dataverse.core.errors.HttpError: - If the Web API request fails. - - Example:: - - # List all relationships - rels = client.tables.list_relationships() - for rel in rels: - print(f"{rel['SchemaName']} ({rel.get('@odata.type')})") - - # Filter by type - one_to_many = client.tables.list_relationships( - filter="RelationshipType eq Microsoft.Dynamics.CRM.RelationshipType'OneToManyRelationship'" - ) - - # Select specific properties - rels = client.tables.list_relationships( - select=["SchemaName", "ReferencedEntity", "ReferencingEntity"] - ) - - - - .. py:method:: list_table_relationships(table: str, *, filter: Optional[str] = None, select: Optional[List[str]] = None) -> List[Dict[str, Any]] - - List all relationships for a specific table. - - Combines one-to-many, many-to-one, and many-to-many relationships - for the given table by querying - ``EntityDefinitions({id})/OneToManyRelationships``, - ``EntityDefinitions({id})/ManyToOneRelationships``, and - ``EntityDefinitions({id})/ManyToManyRelationships``. - - :param table: Schema name of the table (e.g. ``"account"``). - :type table: :class:`str` - :param filter: Optional OData ``$filter`` expression applied to each - sub-request. - :type filter: :class:`str` or None - :param select: Optional list of property names to project via - ``$select``. Values are passed as-is (PascalCase). - :type select: list[str] or None - - :return: Combined list of one-to-many, many-to-one, and many-to-many - relationship metadata dictionaries. - :rtype: list[dict[str, typing.Any]] - - :raises ~PowerPlatform.Dataverse.core.errors.MetadataError: - If the table is not found. - :raises ~PowerPlatform.Dataverse.core.errors.HttpError: - If the Web API request fails. - - Example:: - - # List all relationships for the account table - rels = client.tables.list_table_relationships("account") - for rel in rels: - print(f"{rel['SchemaName']} -> {rel.get('@odata.type')}") - - - diff --git a/docs_local/autoapi/PowerPlatform/Dataverse/utils/index.rst b/docs_local/autoapi/PowerPlatform/Dataverse/utils/index.rst deleted file mode 100644 index bf579628..00000000 --- a/docs_local/autoapi/PowerPlatform/Dataverse/utils/index.rst +++ /dev/null @@ -1,13 +0,0 @@ -PowerPlatform.Dataverse.utils -============================= - -.. py:module:: PowerPlatform.Dataverse.utils - -.. autoapi-nested-parse:: - - Utilities and adapters for the Dataverse SDK. - - Placeholder module for future utility adapters. - - - diff --git a/docs_local/autoapi/PowerPlatform/index.rst b/docs_local/autoapi/PowerPlatform/index.rst deleted file mode 100644 index 6bf6b5a5..00000000 --- a/docs_local/autoapi/PowerPlatform/index.rst +++ /dev/null @@ -1,15 +0,0 @@ -PowerPlatform -============= - -.. py:module:: PowerPlatform - - -Submodules ----------- - -.. toctree:: - :maxdepth: 1 - - /autoapi/PowerPlatform/Dataverse/index - - diff --git a/docs_local/autoapi/index.rst b/docs_local/autoapi/index.rst deleted file mode 100644 index 79277872..00000000 --- a/docs_local/autoapi/index.rst +++ /dev/null @@ -1,11 +0,0 @@ -API Reference -============= - -This page contains auto-generated API reference documentation [#f1]_. - -.. toctree:: - :titlesonly: - - /autoapi/PowerPlatform/index - -.. [#f1] Created with `sphinx-autoapi `_ \ No newline at end of file diff --git a/docs_local/build.log b/docs_local/build.log deleted file mode 100644 index 9d85d230..00000000 --- a/docs_local/build.log +++ /dev/null @@ -1,216 +0,0 @@ -Running Sphinx v9.1.0 -loading translations [en]... done -making output directory... done -[AutoAPI] Reading files... [ 2%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\__init__.py [AutoAPI] Reading files... [ 5%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\client.py [AutoAPI] Reading files... [ 7%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\_skill_installer.py [AutoAPI] Reading files... [ 9%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\__init__.py [AutoAPI] Reading files... [ 11%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\claude_skill\__init__.py [AutoAPI] Reading files... [ 14%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\constants.py [AutoAPI] Reading files... [ 16%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\__init__.py [AutoAPI] Reading files... [ 18%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\config.py [AutoAPI] Reading files... [ 20%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\errors.py [AutoAPI] Reading files... [ 23%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\log_config.py [AutoAPI] Reading files... [ 25%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_auth.py [AutoAPI] Reading files... [ 27%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_error_codes.py [AutoAPI] Reading files... [ 30%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http.py [AutoAPI] Reading files... [ 32%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http_logger.py [AutoAPI] Reading files... [ 34%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\__init__.py [AutoAPI] Reading files... [ 36%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch.py [AutoAPI] Reading files... [ 39%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata.py [AutoAPI] Reading files... [ 41%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_raw_request.py [AutoAPI] Reading files... [ 43%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_relationships.py [AutoAPI] Reading files... [ 45%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_upload.py [AutoAPI] Reading files... [ 48%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\__init__.py [AutoAPI] Reading files... [ 50%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\extensions\__init__.py [AutoAPI] Reading files... [ 52%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\migrate_v0_to_v1.py [AutoAPI] Reading files... [ 55%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\__init__.py [AutoAPI] Reading files... [ 57%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\batch.py [AutoAPI] Reading files... [ 59%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\fetchxml_query.py [AutoAPI] Reading files... [ 61%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\filters.py [AutoAPI] Reading files... [ 64%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\labels.py [AutoAPI] Reading files... [ 66%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\protocol.py [AutoAPI] Reading files... [ 68%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\query_builder.py [AutoAPI] Reading files... [ 70%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\record.py [AutoAPI] Reading files... [ 73%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\relationship.py [AutoAPI] Reading files... [ 75%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\table_info.py [AutoAPI] Reading files... [ 77%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\upsert.py [AutoAPI] Reading files... [ 80%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\__init__.py [AutoAPI] Reading files... [ 82%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\batch.py [AutoAPI] Reading files... [ 84%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\dataframe.py [AutoAPI] Reading files... [ 86%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\files.py [AutoAPI] Reading files... [ 89%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\query.py [AutoAPI] Reading files... [ 91%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\records.py [AutoAPI] Reading files... [ 93%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\tables.py [AutoAPI] Reading files... [ 95%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\__init__.py [AutoAPI] Reading files... [ 98%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\_pandas.py [AutoAPI] Reading files... [100%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\__init__.py -[AutoAPI] Mapping Data... [ 2%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\__init__.py [AutoAPI] Mapping Data... [ 5%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\client.py [AutoAPI] Mapping Data... [ 7%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\_skill_installer.py [AutoAPI] Mapping Data... [ 9%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\__init__.py [AutoAPI] Mapping Data... [ 11%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\claude_skill\__init__.py [AutoAPI] Mapping Data... [ 14%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\constants.py [AutoAPI] Mapping Data... [ 16%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\__init__.py [AutoAPI] Mapping Data... [ 18%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\config.py [AutoAPI] Mapping Data... [ 20%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\errors.py [AutoAPI] Mapping Data... [ 23%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\log_config.py [AutoAPI] Mapping Data... [ 25%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_auth.py [AutoAPI] Mapping Data... [ 27%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_error_codes.py [AutoAPI] Mapping Data... [ 30%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http.py [AutoAPI] Mapping Data... [ 32%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http_logger.py [AutoAPI] Mapping Data... [ 34%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\__init__.py [AutoAPI] Mapping Data... [ 36%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch.py [AutoAPI] Mapping Data... [ 39%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata.py [AutoAPI] Mapping Data... [ 41%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_raw_request.py [AutoAPI] Mapping Data... [ 43%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_relationships.py [AutoAPI] Mapping Data... [ 45%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_upload.py [AutoAPI] Mapping Data... [ 48%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\__init__.py [AutoAPI] Mapping Data... [ 50%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\extensions\__init__.py [AutoAPI] Mapping Data... [ 52%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\migrate_v0_to_v1.py [AutoAPI] Mapping Data... [ 55%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\__init__.py [AutoAPI] Mapping Data... [ 57%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\batch.py [AutoAPI] Mapping Data... [ 59%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\fetchxml_query.py [AutoAPI] Mapping Data... [ 61%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\filters.py [AutoAPI] Mapping Data... [ 64%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\labels.py [AutoAPI] Mapping Data... [ 66%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\protocol.py [AutoAPI] Mapping Data... [ 68%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\query_builder.py [AutoAPI] Mapping Data... [ 70%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\record.py [AutoAPI] Mapping Data... [ 73%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\relationship.py [AutoAPI] Mapping Data... [ 75%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\table_info.py [AutoAPI] Mapping Data... [ 77%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\upsert.py [AutoAPI] Mapping Data... [ 80%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\__init__.py [AutoAPI] Mapping Data... [ 82%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\batch.py [AutoAPI] Mapping Data... [ 84%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\dataframe.py [AutoAPI] Mapping Data... [ 86%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\files.py [AutoAPI] Mapping Data... [ 89%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\query.py [AutoAPI] Mapping Data... [ 91%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\records.py [AutoAPI] Mapping Data... [ 93%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\tables.py [AutoAPI] Mapping Data... [ 95%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\__init__.py [AutoAPI] Mapping Data... [ 98%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\_pandas.py [AutoAPI] Mapping Data... [100%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\__init__.py -[AutoAPI] Rendering Data... [ 3%] PowerPlatform -[AutoAPI] Rendering Data... [ 6%] PowerPlatform.Dataverse -[AutoAPI] Rendering Data... [ 9%] PowerPlatform.Dataverse.core -[AutoAPI] Rendering Data... [ 12%] PowerPlatform.Dataverse.data -[AutoAPI] Rendering Data... [ 15%] PowerPlatform.Dataverse.utils -[AutoAPI] Rendering Data... [ 18%] PowerPlatform.Dataverse.client -[AutoAPI] Rendering Data... [ 21%] PowerPlatform.Dataverse.common -[AutoAPI] Rendering Data... [ 24%] PowerPlatform.Dataverse.models -[AutoAPI] Rendering Data... [ 27%] PowerPlatform.Dataverse.migration -[AutoAPI] Rendering Data... [ 30%] PowerPlatform.Dataverse.extensions -[AutoAPI] Rendering Data... [ 33%] PowerPlatform.Dataverse.operations -[AutoAPI] Rendering Data... [ 36%] PowerPlatform.Dataverse.core.config -[AutoAPI] Rendering Data... [ 39%] PowerPlatform.Dataverse.core.errors -[AutoAPI] Rendering Data... [ 42%] PowerPlatform.Dataverse.claude_skill -[AutoAPI] Rendering Data... [ 45%] PowerPlatform.Dataverse.models.batch -[AutoAPI] Rendering Data... [ 48%] PowerPlatform.Dataverse.models.labels -[AutoAPI] Rendering Data... [ 52%] PowerPlatform.Dataverse.models.record -[AutoAPI] Rendering Data... [ 55%] PowerPlatform.Dataverse.models.upsert -[AutoAPI] Rendering Data... [ 58%] PowerPlatform.Dataverse.models.filters -[AutoAPI] Rendering Data... [ 61%] PowerPlatform.Dataverse.core.log_config -[AutoAPI] Rendering Data... [ 64%] PowerPlatform.Dataverse.models.protocol -[AutoAPI] Rendering Data... [ 67%] PowerPlatform.Dataverse.common.constants -[AutoAPI] Rendering Data... [ 70%] PowerPlatform.Dataverse.operations.batch -[AutoAPI] Rendering Data... [ 73%] PowerPlatform.Dataverse.operations.files -[AutoAPI] Rendering Data... [ 76%] PowerPlatform.Dataverse.operations.query -[AutoAPI] Rendering Data... [ 79%] PowerPlatform.Dataverse.models.table_info -[AutoAPI] Rendering Data... [ 82%] PowerPlatform.Dataverse.operations.tables -[AutoAPI] Rendering Data... [ 85%] PowerPlatform.Dataverse.operations.records -[AutoAPI] Rendering Data... [ 88%] PowerPlatform.Dataverse.models.relationship -[AutoAPI] Rendering Data... [ 91%] PowerPlatform.Dataverse.models.query_builder -[AutoAPI] Rendering Data... [ 94%] PowerPlatform.Dataverse.operations.dataframe -[AutoAPI] Rendering Data... [ 97%] PowerPlatform.Dataverse.models.fetchxml_query -[AutoAPI] Rendering Data... [100%] PowerPlatform.Dataverse.migration.migrate_v0_to_v1 - -[autosummary] generating autosummary for: index.rst -building [mo]: targets for 0 po files that are out of date -writing output...  -building [html]: targets for 1 source files that are out of date -updating environment: [new config] 35 added, 0 changed, 0 removed -reading sources... [ 3%] autoapi/PowerPlatform/Dataverse/claude_skill/index reading sources... [ 6%] autoapi/PowerPlatform/Dataverse/client/index reading sources... [ 9%] autoapi/PowerPlatform/Dataverse/common/constants/index reading sources... [ 11%] autoapi/PowerPlatform/Dataverse/common/index reading sources... [ 14%] autoapi/PowerPlatform/Dataverse/core/config/index reading sources... [ 17%] autoapi/PowerPlatform/Dataverse/core/errors/index reading sources... [ 20%] autoapi/PowerPlatform/Dataverse/core/index reading sources... [ 23%] autoapi/PowerPlatform/Dataverse/core/log_config/index reading sources... [ 26%] autoapi/PowerPlatform/Dataverse/data/index reading sources... [ 29%] autoapi/PowerPlatform/Dataverse/extensions/index reading sources... [ 31%] autoapi/PowerPlatform/Dataverse/index reading sources... [ 34%] autoapi/PowerPlatform/Dataverse/migration/index reading sources... [ 37%] autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index reading sources... [ 40%] autoapi/PowerPlatform/Dataverse/models/batch/index reading sources... [ 43%] autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index reading sources... [ 46%] autoapi/PowerPlatform/Dataverse/models/filters/index reading sources... [ 49%] autoapi/PowerPlatform/Dataverse/models/index reading sources... [ 51%] autoapi/PowerPlatform/Dataverse/models/labels/index reading sources... [ 54%] autoapi/PowerPlatform/Dataverse/models/protocol/index reading sources... [ 57%] autoapi/PowerPlatform/Dataverse/models/query_builder/index reading sources... [ 60%] autoapi/PowerPlatform/Dataverse/models/record/index reading sources... [ 63%] autoapi/PowerPlatform/Dataverse/models/relationship/index reading sources... [ 66%] autoapi/PowerPlatform/Dataverse/models/table_info/index reading sources... [ 69%] autoapi/PowerPlatform/Dataverse/models/upsert/index reading sources... [ 71%] autoapi/PowerPlatform/Dataverse/operations/batch/index reading sources... [ 74%] autoapi/PowerPlatform/Dataverse/operations/dataframe/index reading sources... [ 77%] autoapi/PowerPlatform/Dataverse/operations/files/index reading sources... [ 80%] autoapi/PowerPlatform/Dataverse/operations/index reading sources... [ 83%] autoapi/PowerPlatform/Dataverse/operations/query/index reading sources... [ 86%] autoapi/PowerPlatform/Dataverse/operations/records/index reading sources... [ 89%] autoapi/PowerPlatform/Dataverse/operations/tables/index reading sources... [ 91%] autoapi/PowerPlatform/Dataverse/utils/index reading sources... [ 94%] autoapi/PowerPlatform/index reading sources... [ 97%] autoapi/index reading sources... [100%] index -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:77: ERROR: Unexpected indentation. [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:72: WARNING: Block quote ends without a blank line; unexpected unindent. [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:78: WARNING: Definition list ends without a blank line; unexpected unindent. [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\filters\index.rst:181: ERROR: Malformed table. -Right border not aligned or missing. - -+-----------------+-----------------------------+-------------------------------------+ -| Pattern form | Example | Compiles to | -+=================+=============================+=====================================+ -| ``val%`` | ``like("Contoso%")`` | ``startswith(column,'Contoso')`` | -+-----------------+-----------------------------+-------------------------------------+ -| ``%val`` | ``like("%Ltd")`` | ``endswith(column,'Ltd')`` | -+-----------------+-----------------------------+-------------------------------------+ -| ``%val%`` | ``like("%Corp%")`` | ``contains(column,'Corp')`` | -+-----------------+-----------------------------+-------------------------------------+ -| No wildcard | ``like("Contoso")`` | ``column eq 'Contoso'`` | -+-----------------+-----------------------------+-------------------------------------+ -| Other | ``like("Con%oso")`` | :class:`ValueError` | -+-----------------+-----------------------------+-------------------------------------+ [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\index.rst:354: ERROR: Malformed table. -Right border not aligned or missing. - -+-----------------+-----------------------------+-------------------------------------+ -| Pattern form | Example | Compiles to | -+=================+=============================+=====================================+ -| ``val%`` | ``like("Contoso%")`` | ``startswith(column,'Contoso')`` | -+-----------------+-----------------------------+-------------------------------------+ -| ``%val`` | ``like("%Ltd")`` | ``endswith(column,'Ltd')`` | -+-----------------+-----------------------------+-------------------------------------+ -| ``%val%`` | ``like("%Corp%")`` | ``contains(column,'Corp')`` | -+-----------------+-----------------------------+-------------------------------------+ -| No wildcard | ``like("Contoso")`` | ``column eq 'Contoso'`` | -+-----------------+-----------------------------+-------------------------------------+ -| Other | ``like("Con%oso")`` | :class:`ValueError` | -+-----------------+-----------------------------+-------------------------------------+ [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:1189: WARNING: Inline emphasis start-string without end-string. [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\query\index.rst:114: WARNING: Inline emphasis start-string without end-string. [docutils] -looking for now-outdated files... none found -pickling environment... done -checking consistency... done -preparing documents... done -copying assets...  -copying static files...  -Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\basic.css -Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\documentation_options.js -Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\language_data.js -Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\alabaster.css -copying static files: done -copying extra files...  -copying extra files: done -copying assets: done -writing output... [ 3%] autoapi/PowerPlatform/Dataverse/claude_skill/index writing output... [ 6%] autoapi/PowerPlatform/Dataverse/client/index writing output... [ 9%] autoapi/PowerPlatform/Dataverse/common/constants/index writing output... [ 11%] autoapi/PowerPlatform/Dataverse/common/index writing output... [ 14%] autoapi/PowerPlatform/Dataverse/core/config/index writing output... [ 17%] autoapi/PowerPlatform/Dataverse/core/errors/index writing output... [ 20%] autoapi/PowerPlatform/Dataverse/core/index writing output... [ 23%] autoapi/PowerPlatform/Dataverse/core/log_config/index writing output... [ 26%] autoapi/PowerPlatform/Dataverse/data/index writing output... [ 29%] autoapi/PowerPlatform/Dataverse/extensions/index writing output... [ 31%] autoapi/PowerPlatform/Dataverse/index writing output... [ 34%] autoapi/PowerPlatform/Dataverse/migration/index writing output... [ 37%] autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index writing output... [ 40%] autoapi/PowerPlatform/Dataverse/models/batch/index writing output... [ 43%] autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index writing output... [ 46%] autoapi/PowerPlatform/Dataverse/models/filters/index writing output... [ 49%] autoapi/PowerPlatform/Dataverse/models/index writing output... [ 51%] autoapi/PowerPlatform/Dataverse/models/labels/index writing output... [ 54%] autoapi/PowerPlatform/Dataverse/models/protocol/index writing output... [ 57%] autoapi/PowerPlatform/Dataverse/models/query_builder/index writing output... [ 60%] autoapi/PowerPlatform/Dataverse/models/record/index writing output... [ 63%] autoapi/PowerPlatform/Dataverse/models/relationship/index writing output... [ 66%] autoapi/PowerPlatform/Dataverse/models/table_info/index writing output... [ 69%] autoapi/PowerPlatform/Dataverse/models/upsert/index writing output... [ 71%] autoapi/PowerPlatform/Dataverse/operations/batch/index writing output... [ 74%] autoapi/PowerPlatform/Dataverse/operations/dataframe/index writing output... [ 77%] autoapi/PowerPlatform/Dataverse/operations/files/index writing output... [ 80%] autoapi/PowerPlatform/Dataverse/operations/index writing output... [ 83%] autoapi/PowerPlatform/Dataverse/operations/query/index writing output... [ 86%] autoapi/PowerPlatform/Dataverse/operations/records/index writing output... [ 89%] autoapi/PowerPlatform/Dataverse/operations/tables/index writing output... [ 91%] autoapi/PowerPlatform/Dataverse/utils/index writing output... [ 94%] autoapi/PowerPlatform/index writing output... [ 97%] autoapi/index writing output... [100%] index -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\client\index.rst:18: WARNING: py:class reference target not found: azure.core.credentials.TokenCredential [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\client\index.rst:22: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._odata._ODataClient [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\client\index.rst:16: WARNING: py:class reference target not found: azure.core.credentials.TokenCredential [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:33::1: WARNING: py:class reference target not found: Record [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:42::1: WARNING: py:class reference target not found: ColumnProxy [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:46: WARNING: more than one target found for cross-reference 'ColumnProxy': PowerPlatform.Dataverse.models.filters.ColumnProxy, PowerPlatform.Dataverse.models.ColumnProxy [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:48: WARNING: py:class reference target not found: ColumnProxy [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:61: WARNING: py:class reference target not found: ColumnProxy [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:65: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:73: WARNING: py:class reference target not found: FilterExpression [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:152: WARNING: more than one target found for cross-reference 'Record': PowerPlatform.Dataverse.models.Record, PowerPlatform.Dataverse.models.record.Record [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:154: WARNING: py:class reference target not found: Record [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:162: WARNING: py:class reference target not found: Record [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:165: WARNING: more than one target found for cross-reference 'Record': PowerPlatform.Dataverse.models.Record, PowerPlatform.Dataverse.models.record.Record [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:169: WARNING: more than one target found for cross-reference 'Record': PowerPlatform.Dataverse.models.Record, PowerPlatform.Dataverse.models.record.Record [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:44: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:113: WARNING: py:class reference target not found: pathlib.Path [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\fetchxml_query\index.rst:37: WARNING: py:class reference target not found: QueryResult [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\fetchxml_query\index.rst:55: WARNING: py:class reference target not found: QueryResult [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\fetchxml_query\index.rst:67: WARNING: py:class reference target not found: QueryResult [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\index.rst:965: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\index.rst:81: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.QueryResult, PowerPlatform.Dataverse.models.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.QueryResult, PowerPlatform.Dataverse.models.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.QueryResult, PowerPlatform.Dataverse.models.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:487: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\record\index.rst:22: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\relationship\index.rst:25: WARNING: more than one target found for cross-reference 'Label': PowerPlatform.Dataverse.models.Label, PowerPlatform.Dataverse.models.labels.Label [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\relationship\index.rst:25: WARNING: more than one target found for cross-reference 'Label': PowerPlatform.Dataverse.models.Label, PowerPlatform.Dataverse.models.labels.Label [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:30: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:93: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:275: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:290: WARNING: py:class reference target not found: FilterExpression [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:413: WARNING: py:attr reference target not found: BatchResult.responses [ref.attr] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:427: WARNING: py:attr reference target not found: BatchResult.responses [ref.attr] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:563: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:585: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:617: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:28: WARNING: more than one target found for cross-reference 'ValidationError': PowerPlatform.Dataverse.core.errors.ValidationError, PowerPlatform.Dataverse.core.ValidationError [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:28: WARNING: more than one target found for cross-reference 'MetadataError': PowerPlatform.Dataverse.core.errors.MetadataError, PowerPlatform.Dataverse.core.MetadataError [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:28: WARNING: more than one target found for cross-reference 'HttpError': PowerPlatform.Dataverse.core.errors.HttpError, PowerPlatform.Dataverse.core.HttpError [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:54: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:93: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:154: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:154: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:189: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:248: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:76: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:98: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:130: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:368: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:383: WARNING: py:class reference target not found: FilterExpression [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:50: WARNING: more than one target found for cross-reference 'ValidationError': PowerPlatform.Dataverse.core.errors.ValidationError, PowerPlatform.Dataverse.core.ValidationError [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:50: WARNING: more than one target found for cross-reference 'MetadataError': PowerPlatform.Dataverse.core.errors.MetadataError, PowerPlatform.Dataverse.core.MetadataError [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:50: WARNING: more than one target found for cross-reference 'HttpError': PowerPlatform.Dataverse.core.errors.HttpError, PowerPlatform.Dataverse.core.HttpError [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:585: WARNING: py:attr reference target not found: BatchResult.responses [ref.attr] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:599: WARNING: py:attr reference target not found: BatchResult.responses [ref.attr] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:680: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:701: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:795: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:834: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:895: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:895: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:930: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:989: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: collections.abc.Iterable [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:1694: WARNING: py:class reference target not found: QueryResult [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:1696: WARNING: py:class reference target not found: QueryResult [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:1701: WARNING: py:class reference target not found: FilterExpression [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:1718: WARNING: py:class reference target not found: QueryResult [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:1740: WARNING: py:class reference target not found: QueryResult [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:1748: WARNING: py:class reference target not found: FilterExpression [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:1765: WARNING: py:class reference target not found: QueryResult [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: more than one target found for cross-reference 'UpsertItem': PowerPlatform.Dataverse.models.UpsertItem, PowerPlatform.Dataverse.models.upsert.UpsertItem [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:2308: WARNING: py:attr reference target not found: AlternateKeyInfo.status [ref.attr] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:: WARNING: py:class reference target not found: collections.abc.Iterable [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:296: WARNING: py:class reference target not found: QueryResult [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:298: WARNING: py:class reference target not found: QueryResult [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:303: WARNING: py:class reference target not found: FilterExpression [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:320: WARNING: py:class reference target not found: QueryResult [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:342: WARNING: py:class reference target not found: QueryResult [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:350: WARNING: py:class reference target not found: FilterExpression [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:367: WARNING: py:class reference target not found: QueryResult [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:: WARNING: more than one target found for cross-reference 'UpsertItem': PowerPlatform.Dataverse.models.UpsertItem, PowerPlatform.Dataverse.models.upsert.UpsertItem [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\tables\index.rst:471: WARNING: py:attr reference target not found: AlternateKeyInfo.status [ref.attr] -generating indices... genindex py-modindex done -writing additional pages... search done -dumping search index in English (code: en)... done -dumping object inventory... done -build succeeded, 119 warnings. - -The HTML pages are in docs_local\_build. diff --git a/docs_local/build_after_fixes.log b/docs_local/build_after_fixes.log deleted file mode 100644 index 5aff2e13..00000000 --- a/docs_local/build_after_fixes.log +++ /dev/null @@ -1,125 +0,0 @@ -Running Sphinx v9.1.0 -loading translations [en]... done -making output directory... done -[AutoAPI] Reading files... [ 2%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\__init__.py [AutoAPI] Reading files... [ 5%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\client.py [AutoAPI] Reading files... [ 7%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\_skill_installer.py [AutoAPI] Reading files... [ 9%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\__init__.py [AutoAPI] Reading files... [ 11%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\claude_skill\__init__.py [AutoAPI] Reading files... [ 14%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\constants.py [AutoAPI] Reading files... [ 16%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\__init__.py [AutoAPI] Reading files... [ 18%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\config.py [AutoAPI] Reading files... [ 20%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\errors.py [AutoAPI] Reading files... [ 23%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\log_config.py [AutoAPI] Reading files... [ 25%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_auth.py [AutoAPI] Reading files... [ 27%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_error_codes.py [AutoAPI] Reading files... [ 30%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http.py [AutoAPI] Reading files... [ 32%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http_logger.py [AutoAPI] Reading files... [ 34%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\__init__.py [AutoAPI] Reading files... [ 36%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch.py [AutoAPI] Reading files... [ 39%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata.py [AutoAPI] Reading files... [ 41%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_raw_request.py [AutoAPI] Reading files... [ 43%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_relationships.py [AutoAPI] Reading files... [ 45%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_upload.py [AutoAPI] Reading files... [ 48%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\__init__.py [AutoAPI] Reading files... [ 50%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\extensions\__init__.py [AutoAPI] Reading files... [ 52%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\migrate_v0_to_v1.py [AutoAPI] Reading files... [ 55%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\__init__.py [AutoAPI] Reading files... [ 57%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\batch.py [AutoAPI] Reading files... [ 59%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\fetchxml_query.py [AutoAPI] Reading files... [ 61%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\filters.py [AutoAPI] Reading files... [ 64%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\labels.py [AutoAPI] Reading files... [ 66%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\protocol.py [AutoAPI] Reading files... [ 68%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\query_builder.py [AutoAPI] Reading files... [ 70%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\record.py [AutoAPI] Reading files... [ 73%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\relationship.py [AutoAPI] Reading files... [ 75%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\table_info.py [AutoAPI] Reading files... [ 77%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\upsert.py [AutoAPI] Reading files... [ 80%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\__init__.py [AutoAPI] Reading files... [ 82%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\batch.py [AutoAPI] Reading files... [ 84%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\dataframe.py [AutoAPI] Reading files... [ 86%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\files.py [AutoAPI] Reading files... [ 89%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\query.py [AutoAPI] Reading files... [ 91%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\records.py [AutoAPI] Reading files... [ 93%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\tables.py [AutoAPI] Reading files... [ 95%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\__init__.py [AutoAPI] Reading files... [ 98%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\_pandas.py [AutoAPI] Reading files... [100%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\__init__.py -[AutoAPI] Mapping Data... [ 2%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\__init__.py [AutoAPI] Mapping Data... [ 5%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\client.py [AutoAPI] Mapping Data... [ 7%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\_skill_installer.py [AutoAPI] Mapping Data... [ 9%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\__init__.py [AutoAPI] Mapping Data... [ 11%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\claude_skill\__init__.py [AutoAPI] Mapping Data... [ 14%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\constants.py [AutoAPI] Mapping Data... [ 16%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\__init__.py [AutoAPI] Mapping Data... [ 18%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\config.py [AutoAPI] Mapping Data... [ 20%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\errors.py [AutoAPI] Mapping Data... [ 23%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\log_config.py [AutoAPI] Mapping Data... [ 25%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_auth.py [AutoAPI] Mapping Data... [ 27%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_error_codes.py [AutoAPI] Mapping Data... [ 30%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http.py [AutoAPI] Mapping Data... [ 32%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http_logger.py [AutoAPI] Mapping Data... [ 34%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\__init__.py [AutoAPI] Mapping Data... [ 36%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch.py [AutoAPI] Mapping Data... [ 39%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata.py [AutoAPI] Mapping Data... [ 41%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_raw_request.py [AutoAPI] Mapping Data... [ 43%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_relationships.py [AutoAPI] Mapping Data... [ 45%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_upload.py [AutoAPI] Mapping Data... [ 48%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\__init__.py [AutoAPI] Mapping Data... [ 50%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\extensions\__init__.py [AutoAPI] Mapping Data... [ 52%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\migrate_v0_to_v1.py [AutoAPI] Mapping Data... [ 55%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\__init__.py [AutoAPI] Mapping Data... [ 57%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\batch.py [AutoAPI] Mapping Data... [ 59%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\fetchxml_query.py [AutoAPI] Mapping Data... [ 61%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\filters.py [AutoAPI] Mapping Data... [ 64%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\labels.py [AutoAPI] Mapping Data... [ 66%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\protocol.py [AutoAPI] Mapping Data... [ 68%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\query_builder.py [AutoAPI] Mapping Data... [ 70%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\record.py [AutoAPI] Mapping Data... [ 73%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\relationship.py [AutoAPI] Mapping Data... [ 75%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\table_info.py [AutoAPI] Mapping Data... [ 77%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\upsert.py [AutoAPI] Mapping Data... [ 80%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\__init__.py [AutoAPI] Mapping Data... [ 82%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\batch.py [AutoAPI] Mapping Data... [ 84%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\dataframe.py [AutoAPI] Mapping Data... [ 86%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\files.py [AutoAPI] Mapping Data... [ 89%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\query.py [AutoAPI] Mapping Data... [ 91%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\records.py [AutoAPI] Mapping Data... [ 93%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\tables.py [AutoAPI] Mapping Data... [ 95%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\__init__.py [AutoAPI] Mapping Data... [ 98%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\_pandas.py [AutoAPI] Mapping Data... [100%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\__init__.py -[AutoAPI] Rendering Data... [ 3%] PowerPlatform -[AutoAPI] Rendering Data... [ 6%] PowerPlatform.Dataverse -[AutoAPI] Rendering Data... [ 9%] PowerPlatform.Dataverse.core -[AutoAPI] Rendering Data... [ 12%] PowerPlatform.Dataverse.data -[AutoAPI] Rendering Data... [ 15%] PowerPlatform.Dataverse.utils -[AutoAPI] Rendering Data... [ 18%] PowerPlatform.Dataverse.client -[AutoAPI] Rendering Data... [ 21%] PowerPlatform.Dataverse.common -[AutoAPI] Rendering Data... [ 24%] PowerPlatform.Dataverse.models -[AutoAPI] Rendering Data... [ 27%] PowerPlatform.Dataverse.migration -[AutoAPI] Rendering Data... [ 30%] PowerPlatform.Dataverse.extensions -[AutoAPI] Rendering Data... [ 33%] PowerPlatform.Dataverse.operations -[AutoAPI] Rendering Data... [ 36%] PowerPlatform.Dataverse.core.config -[AutoAPI] Rendering Data... [ 39%] PowerPlatform.Dataverse.core.errors -[AutoAPI] Rendering Data... [ 42%] PowerPlatform.Dataverse.claude_skill -[AutoAPI] Rendering Data... [ 45%] PowerPlatform.Dataverse.models.batch -[AutoAPI] Rendering Data... [ 48%] PowerPlatform.Dataverse.models.labels -[AutoAPI] Rendering Data... [ 52%] PowerPlatform.Dataverse.models.record -[AutoAPI] Rendering Data... [ 55%] PowerPlatform.Dataverse.models.upsert -[AutoAPI] Rendering Data... [ 58%] PowerPlatform.Dataverse.models.filters -[AutoAPI] Rendering Data... [ 61%] PowerPlatform.Dataverse.core.log_config -[AutoAPI] Rendering Data... [ 64%] PowerPlatform.Dataverse.models.protocol -[AutoAPI] Rendering Data... [ 67%] PowerPlatform.Dataverse.common.constants -[AutoAPI] Rendering Data... [ 70%] PowerPlatform.Dataverse.operations.batch -[AutoAPI] Rendering Data... [ 73%] PowerPlatform.Dataverse.operations.files -[AutoAPI] Rendering Data... [ 76%] PowerPlatform.Dataverse.operations.query -[AutoAPI] Rendering Data... [ 79%] PowerPlatform.Dataverse.models.table_info -[AutoAPI] Rendering Data... [ 82%] PowerPlatform.Dataverse.operations.tables -[AutoAPI] Rendering Data... [ 85%] PowerPlatform.Dataverse.operations.records -[AutoAPI] Rendering Data... [ 88%] PowerPlatform.Dataverse.models.relationship -[AutoAPI] Rendering Data... [ 91%] PowerPlatform.Dataverse.models.query_builder -[AutoAPI] Rendering Data... [ 94%] PowerPlatform.Dataverse.operations.dataframe -[AutoAPI] Rendering Data... [ 97%] PowerPlatform.Dataverse.models.fetchxml_query -[AutoAPI] Rendering Data... [100%] PowerPlatform.Dataverse.migration.migrate_v0_to_v1 - -[autosummary] generating autosummary for: index.rst -building [mo]: targets for 0 po files that are out of date -writing output...  -building [html]: targets for 1 source files that are out of date -updating environment: [new config] 35 added, 0 changed, 0 removed -reading sources... [ 3%] autoapi/PowerPlatform/Dataverse/claude_skill/index reading sources... [ 6%] autoapi/PowerPlatform/Dataverse/client/index reading sources... [ 9%] autoapi/PowerPlatform/Dataverse/common/constants/index reading sources... [ 11%] autoapi/PowerPlatform/Dataverse/common/index reading sources... [ 14%] autoapi/PowerPlatform/Dataverse/core/config/index reading sources... [ 17%] autoapi/PowerPlatform/Dataverse/core/errors/index reading sources... [ 20%] autoapi/PowerPlatform/Dataverse/core/index reading sources... [ 23%] autoapi/PowerPlatform/Dataverse/core/log_config/index reading sources... [ 26%] autoapi/PowerPlatform/Dataverse/data/index reading sources... [ 29%] autoapi/PowerPlatform/Dataverse/extensions/index reading sources... [ 31%] autoapi/PowerPlatform/Dataverse/index reading sources... [ 34%] autoapi/PowerPlatform/Dataverse/migration/index reading sources... [ 37%] autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index reading sources... [ 40%] autoapi/PowerPlatform/Dataverse/models/batch/index reading sources... [ 43%] autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index reading sources... [ 46%] autoapi/PowerPlatform/Dataverse/models/filters/index reading sources... [ 49%] autoapi/PowerPlatform/Dataverse/models/index reading sources... [ 51%] autoapi/PowerPlatform/Dataverse/models/labels/index reading sources... [ 54%] autoapi/PowerPlatform/Dataverse/models/protocol/index reading sources... [ 57%] autoapi/PowerPlatform/Dataverse/models/query_builder/index reading sources... [ 60%] autoapi/PowerPlatform/Dataverse/models/record/index reading sources... [ 63%] autoapi/PowerPlatform/Dataverse/models/relationship/index reading sources... [ 66%] autoapi/PowerPlatform/Dataverse/models/table_info/index reading sources... [ 69%] autoapi/PowerPlatform/Dataverse/models/upsert/index reading sources... [ 71%] autoapi/PowerPlatform/Dataverse/operations/batch/index reading sources... [ 74%] autoapi/PowerPlatform/Dataverse/operations/dataframe/index reading sources... [ 77%] autoapi/PowerPlatform/Dataverse/operations/files/index reading sources... [ 80%] autoapi/PowerPlatform/Dataverse/operations/index reading sources... [ 83%] autoapi/PowerPlatform/Dataverse/operations/query/index reading sources... [ 86%] autoapi/PowerPlatform/Dataverse/operations/records/index reading sources... [ 89%] autoapi/PowerPlatform/Dataverse/operations/tables/index reading sources... [ 91%] autoapi/PowerPlatform/Dataverse/utils/index reading sources... [ 94%] autoapi/PowerPlatform/index reading sources... [ 97%] autoapi/index reading sources... [100%] index -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:77: ERROR: Unexpected indentation. [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:72: WARNING: Block quote ends without a blank line; unexpected unindent. [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:78: WARNING: Definition list ends without a blank line; unexpected unindent. [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\filters\index.rst:181: ERROR: Malformed table. -Right border not aligned or missing. - -+-----------------+-----------------------------+-------------------------------------+ -| Pattern form | Example | Compiles to | -+=================+=============================+=====================================+ -| ``val%`` | ``like("Contoso%")`` | ``startswith(column,'Contoso')`` | -+-----------------+-----------------------------+-------------------------------------+ -| ``%val`` | ``like("%Ltd")`` | ``endswith(column,'Ltd')`` | -+-----------------+-----------------------------+-------------------------------------+ -| ``%val%`` | ``like("%Corp%")`` | ``contains(column,'Corp')`` | -+-----------------+-----------------------------+-------------------------------------+ -| No wildcard | ``like("Contoso")`` | ``column eq 'Contoso'`` | -+-----------------+-----------------------------+-------------------------------------+ -| Other | ``like("Con%oso")`` | :class:`ValueError` | -+-----------------+-----------------------------+-------------------------------------+ [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\query\index.rst:114: WARNING: Inline emphasis start-string without end-string. [docutils] -looking for now-outdated files... none found -pickling environment... done -checking consistency... done -preparing documents... done -copying assets...  -copying static files...  -Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\basic.css -Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\documentation_options.js -Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\language_data.js -Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\alabaster.css -copying static files: done -copying extra files...  -copying extra files: done -copying assets: done -writing output... [ 3%] autoapi/PowerPlatform/Dataverse/claude_skill/index writing output... [ 6%] autoapi/PowerPlatform/Dataverse/client/index writing output... [ 9%] autoapi/PowerPlatform/Dataverse/common/constants/index writing output... [ 11%] autoapi/PowerPlatform/Dataverse/common/index writing output... [ 14%] autoapi/PowerPlatform/Dataverse/core/config/index writing output... [ 17%] autoapi/PowerPlatform/Dataverse/core/errors/index writing output... [ 20%] autoapi/PowerPlatform/Dataverse/core/index writing output... [ 23%] autoapi/PowerPlatform/Dataverse/core/log_config/index writing output... [ 26%] autoapi/PowerPlatform/Dataverse/data/index writing output... [ 29%] autoapi/PowerPlatform/Dataverse/extensions/index writing output... [ 31%] autoapi/PowerPlatform/Dataverse/index writing output... [ 34%] autoapi/PowerPlatform/Dataverse/migration/index writing output... [ 37%] autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index writing output... [ 40%] autoapi/PowerPlatform/Dataverse/models/batch/index writing output... [ 43%] autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index writing output... [ 46%] autoapi/PowerPlatform/Dataverse/models/filters/index writing output... [ 49%] autoapi/PowerPlatform/Dataverse/models/index writing output... [ 51%] autoapi/PowerPlatform/Dataverse/models/labels/index writing output... [ 54%] autoapi/PowerPlatform/Dataverse/models/protocol/index writing output... [ 57%] autoapi/PowerPlatform/Dataverse/models/query_builder/index writing output... [ 60%] autoapi/PowerPlatform/Dataverse/models/record/index writing output... [ 63%] autoapi/PowerPlatform/Dataverse/models/relationship/index writing output... [ 66%] autoapi/PowerPlatform/Dataverse/models/table_info/index writing output... [ 69%] autoapi/PowerPlatform/Dataverse/models/upsert/index writing output... [ 71%] autoapi/PowerPlatform/Dataverse/operations/batch/index writing output... [ 74%] autoapi/PowerPlatform/Dataverse/operations/dataframe/index writing output... [ 77%] autoapi/PowerPlatform/Dataverse/operations/files/index writing output... [ 80%] autoapi/PowerPlatform/Dataverse/operations/index writing output... [ 83%] autoapi/PowerPlatform/Dataverse/operations/query/index writing output... [ 86%] autoapi/PowerPlatform/Dataverse/operations/records/index writing output... [ 89%] autoapi/PowerPlatform/Dataverse/operations/tables/index writing output... [ 91%] autoapi/PowerPlatform/Dataverse/utils/index writing output... [ 94%] autoapi/PowerPlatform/index writing output... [ 97%] autoapi/index writing output... [100%] index -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\client\index.rst:18: WARNING: py:class reference target not found: azure.core.credentials.TokenCredential [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\client\index.rst:16: WARNING: py:class reference target not found: azure.core.credentials.TokenCredential [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:33::1: WARNING: py:class reference target not found: Record [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:42::1: WARNING: py:class reference target not found: ColumnProxy [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:48: WARNING: py:class reference target not found: ColumnProxy [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:61: WARNING: py:class reference target not found: ColumnProxy [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:73: WARNING: py:class reference target not found: FilterExpression [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:154: WARNING: py:class reference target not found: Record [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:162: WARNING: py:class reference target not found: Record [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:44: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:113: WARNING: py:class reference target not found: pathlib.Path [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:487: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\record\index.rst:22: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:30: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:93: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:563: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:585: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:617: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:54: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:93: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:154: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:154: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:189: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:248: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:: WARNING: py:class reference target not found: collections.abc.Iterable [ref.class] -generating indices... genindex py-modindex done -writing additional pages... search done -dumping search index in English (code: en)... done -dumping object inventory... done -build succeeded, 43 warnings. - -The HTML pages are in docs_local\_build. diff --git a/docs_local/build_final.log b/docs_local/build_final.log deleted file mode 100644 index 6c418776..00000000 --- a/docs_local/build_final.log +++ /dev/null @@ -1,130 +0,0 @@ -Running Sphinx v9.1.0 -loading translations [en]... done -making output directory... done -[AutoAPI] Reading files... [ 2%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\__init__.py [AutoAPI] Reading files... [ 4%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\client.py [AutoAPI] Reading files... [ 7%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\_skill_installer.py [AutoAPI] Reading files... [ 9%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\__init__.py [AutoAPI] Reading files... [ 11%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\claude_skill\__init__.py [AutoAPI] Reading files... [ 13%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\constants.py [AutoAPI] Reading files... [ 15%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\__init__.py [AutoAPI] Reading files... [ 17%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\config.py [AutoAPI] Reading files... [ 20%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\errors.py [AutoAPI] Reading files... [ 22%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\log_config.py [AutoAPI] Reading files... [ 24%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_auth.py [AutoAPI] Reading files... [ 26%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_error_codes.py [AutoAPI] Reading files... [ 28%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http.py [AutoAPI] Reading files... [ 30%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http_logger.py [AutoAPI] Reading files... [ 33%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\__init__.py [AutoAPI] Reading files... [ 35%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch.py [AutoAPI] Reading files... [ 37%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch_base.py [AutoAPI] Reading files... [ 39%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata.py [AutoAPI] Reading files... [ 41%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata_base.py [AutoAPI] Reading files... [ 43%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_raw_request.py [AutoAPI] Reading files... [ 46%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_relationships.py [AutoAPI] Reading files... [ 48%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_upload.py [AutoAPI] Reading files... [ 50%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\__init__.py [AutoAPI] Reading files... [ 52%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\extensions\__init__.py [AutoAPI] Reading files... [ 54%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\migrate_v0_to_v1.py [AutoAPI] Reading files... [ 57%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\__init__.py [AutoAPI] Reading files... [ 59%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\batch.py [AutoAPI] Reading files... [ 61%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\fetchxml_query.py [AutoAPI] Reading files... [ 63%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\filters.py [AutoAPI] Reading files... [ 65%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\labels.py [AutoAPI] Reading files... [ 67%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\protocol.py [AutoAPI] Reading files... [ 70%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\query_builder.py [AutoAPI] Reading files... [ 72%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\record.py [AutoAPI] Reading files... [ 74%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\relationship.py [AutoAPI] Reading files... [ 76%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\table_info.py [AutoAPI] Reading files... [ 78%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\upsert.py [AutoAPI] Reading files... [ 80%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\__init__.py [AutoAPI] Reading files... [ 83%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\batch.py [AutoAPI] Reading files... [ 85%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\dataframe.py [AutoAPI] Reading files... [ 87%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\files.py [AutoAPI] Reading files... [ 89%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\query.py [AutoAPI] Reading files... [ 91%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\records.py [AutoAPI] Reading files... [ 93%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\tables.py [AutoAPI] Reading files... [ 96%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\__init__.py [AutoAPI] Reading files... [ 98%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\_pandas.py [AutoAPI] Reading files... [100%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\__init__.py -[AutoAPI] Mapping Data... [ 2%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\__init__.py [AutoAPI] Mapping Data... [ 4%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\client.py [AutoAPI] Mapping Data... [ 7%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\_skill_installer.py [AutoAPI] Mapping Data... [ 9%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\__init__.py [AutoAPI] Mapping Data... [ 11%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\claude_skill\__init__.py [AutoAPI] Mapping Data... [ 13%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\constants.py [AutoAPI] Mapping Data... [ 15%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\__init__.py [AutoAPI] Mapping Data... [ 17%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\config.py [AutoAPI] Mapping Data... [ 20%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\errors.py [AutoAPI] Mapping Data... [ 22%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\log_config.py [AutoAPI] Mapping Data... [ 24%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_auth.py [AutoAPI] Mapping Data... [ 26%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_error_codes.py [AutoAPI] Mapping Data... [ 28%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http.py [AutoAPI] Mapping Data... [ 30%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http_logger.py [AutoAPI] Mapping Data... [ 33%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\__init__.py [AutoAPI] Mapping Data... [ 35%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch.py [AutoAPI] Mapping Data... [ 37%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch_base.py [AutoAPI] Mapping Data... [ 39%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata.py [AutoAPI] Mapping Data... [ 41%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata_base.py [AutoAPI] Mapping Data... [ 43%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_raw_request.py [AutoAPI] Mapping Data... [ 46%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_relationships.py [AutoAPI] Mapping Data... [ 48%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_upload.py [AutoAPI] Mapping Data... [ 50%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\__init__.py [AutoAPI] Mapping Data... [ 52%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\extensions\__init__.py [AutoAPI] Mapping Data... [ 54%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\migrate_v0_to_v1.py [AutoAPI] Mapping Data... [ 57%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\__init__.py [AutoAPI] Mapping Data... [ 59%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\batch.py [AutoAPI] Mapping Data... [ 61%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\fetchxml_query.py [AutoAPI] Mapping Data... [ 63%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\filters.py [AutoAPI] Mapping Data... [ 65%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\labels.py [AutoAPI] Mapping Data... [ 67%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\protocol.py [AutoAPI] Mapping Data... [ 70%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\query_builder.py [AutoAPI] Mapping Data... [ 72%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\record.py [AutoAPI] Mapping Data... [ 74%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\relationship.py [AutoAPI] Mapping Data... [ 76%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\table_info.py [AutoAPI] Mapping Data... [ 78%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\upsert.py [AutoAPI] Mapping Data... [ 80%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\__init__.py [AutoAPI] Mapping Data... [ 83%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\batch.py [AutoAPI] Mapping Data... [ 85%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\dataframe.py [AutoAPI] Mapping Data... [ 87%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\files.py [AutoAPI] Mapping Data... [ 89%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\query.py [AutoAPI] Mapping Data... [ 91%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\records.py [AutoAPI] Mapping Data... [ 93%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\tables.py [AutoAPI] Mapping Data... [ 96%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\__init__.py [AutoAPI] Mapping Data... [ 98%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\_pandas.py [AutoAPI] Mapping Data... [100%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\__init__.py -[AutoAPI] Rendering Data... [ 3%] PowerPlatform -[AutoAPI] Rendering Data... [ 6%] PowerPlatform.Dataverse -[AutoAPI] Rendering Data... [ 9%] PowerPlatform.Dataverse.core -[AutoAPI] Rendering Data... [ 12%] PowerPlatform.Dataverse.data -[AutoAPI] Rendering Data... [ 15%] PowerPlatform.Dataverse.utils -[AutoAPI] Rendering Data... [ 18%] PowerPlatform.Dataverse.client -[AutoAPI] Rendering Data... [ 21%] PowerPlatform.Dataverse.common -[AutoAPI] Rendering Data... [ 24%] PowerPlatform.Dataverse.models -[AutoAPI] Rendering Data... [ 27%] PowerPlatform.Dataverse.migration -[AutoAPI] Rendering Data... [ 30%] PowerPlatform.Dataverse.extensions -[AutoAPI] Rendering Data... [ 33%] PowerPlatform.Dataverse.operations -[AutoAPI] Rendering Data... [ 36%] PowerPlatform.Dataverse.core.config -[AutoAPI] Rendering Data... [ 39%] PowerPlatform.Dataverse.core.errors -[AutoAPI] Rendering Data... [ 42%] PowerPlatform.Dataverse.claude_skill -[AutoAPI] Rendering Data... [ 45%] PowerPlatform.Dataverse.models.batch -[AutoAPI] Rendering Data... [ 48%] PowerPlatform.Dataverse.models.labels -[AutoAPI] Rendering Data... [ 52%] PowerPlatform.Dataverse.models.record -[AutoAPI] Rendering Data... [ 55%] PowerPlatform.Dataverse.models.upsert -[AutoAPI] Rendering Data... [ 58%] PowerPlatform.Dataverse.models.filters -[AutoAPI] Rendering Data... [ 61%] PowerPlatform.Dataverse.core.log_config -[AutoAPI] Rendering Data... [ 64%] PowerPlatform.Dataverse.models.protocol -[AutoAPI] Rendering Data... [ 67%] PowerPlatform.Dataverse.common.constants -[AutoAPI] Rendering Data... [ 70%] PowerPlatform.Dataverse.operations.batch -[AutoAPI] Rendering Data... [ 73%] PowerPlatform.Dataverse.operations.files -[AutoAPI] Rendering Data... [ 76%] PowerPlatform.Dataverse.operations.query -[AutoAPI] Rendering Data... [ 79%] PowerPlatform.Dataverse.models.table_info -[AutoAPI] Rendering Data... [ 82%] PowerPlatform.Dataverse.operations.tables -[AutoAPI] Rendering Data... [ 85%] PowerPlatform.Dataverse.operations.records -[AutoAPI] Rendering Data... [ 88%] PowerPlatform.Dataverse.models.relationship -[AutoAPI] Rendering Data... [ 91%] PowerPlatform.Dataverse.models.query_builder -[AutoAPI] Rendering Data... [ 94%] PowerPlatform.Dataverse.operations.dataframe -[AutoAPI] Rendering Data... [ 97%] PowerPlatform.Dataverse.models.fetchxml_query -[AutoAPI] Rendering Data... [100%] PowerPlatform.Dataverse.migration.migrate_v0_to_v1 - -[autosummary] generating autosummary for: index.rst -building [mo]: targets for 0 po files that are out of date -writing output...  -building [html]: targets for 1 source files that are out of date -updating environment: [new config] 35 added, 0 changed, 0 removed -reading sources... [ 3%] autoapi/PowerPlatform/Dataverse/claude_skill/index reading sources... [ 6%] autoapi/PowerPlatform/Dataverse/client/index reading sources... [ 9%] autoapi/PowerPlatform/Dataverse/common/constants/index reading sources... [ 11%] autoapi/PowerPlatform/Dataverse/common/index reading sources... [ 14%] autoapi/PowerPlatform/Dataverse/core/config/index reading sources... [ 17%] autoapi/PowerPlatform/Dataverse/core/errors/index reading sources... [ 20%] autoapi/PowerPlatform/Dataverse/core/index reading sources... [ 23%] autoapi/PowerPlatform/Dataverse/core/log_config/index reading sources... [ 26%] autoapi/PowerPlatform/Dataverse/data/index reading sources... [ 29%] autoapi/PowerPlatform/Dataverse/extensions/index reading sources... [ 31%] autoapi/PowerPlatform/Dataverse/index reading sources... [ 34%] autoapi/PowerPlatform/Dataverse/migration/index reading sources... [ 37%] autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index reading sources... [ 40%] autoapi/PowerPlatform/Dataverse/models/batch/index reading sources... [ 43%] autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index reading sources... [ 46%] autoapi/PowerPlatform/Dataverse/models/filters/index reading sources... [ 49%] autoapi/PowerPlatform/Dataverse/models/index reading sources... [ 51%] autoapi/PowerPlatform/Dataverse/models/labels/index reading sources... [ 54%] autoapi/PowerPlatform/Dataverse/models/protocol/index reading sources... [ 57%] autoapi/PowerPlatform/Dataverse/models/query_builder/index reading sources... [ 60%] autoapi/PowerPlatform/Dataverse/models/record/index reading sources... [ 63%] autoapi/PowerPlatform/Dataverse/models/relationship/index reading sources... [ 66%] autoapi/PowerPlatform/Dataverse/models/table_info/index reading sources... [ 69%] autoapi/PowerPlatform/Dataverse/models/upsert/index reading sources... [ 71%] autoapi/PowerPlatform/Dataverse/operations/batch/index reading sources... [ 74%] autoapi/PowerPlatform/Dataverse/operations/dataframe/index reading sources... [ 77%] autoapi/PowerPlatform/Dataverse/operations/files/index reading sources... [ 80%] autoapi/PowerPlatform/Dataverse/operations/index reading sources... [ 83%] autoapi/PowerPlatform/Dataverse/operations/query/index reading sources... [ 86%] autoapi/PowerPlatform/Dataverse/operations/records/index reading sources... [ 89%] autoapi/PowerPlatform/Dataverse/operations/tables/index reading sources... [ 91%] autoapi/PowerPlatform/Dataverse/utils/index reading sources... [ 94%] autoapi/PowerPlatform/index reading sources... [ 97%] autoapi/index reading sources... [100%] index -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:77: ERROR: Unexpected indentation. [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:72: WARNING: Block quote ends without a blank line; unexpected unindent. [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:78: WARNING: Definition list ends without a blank line; unexpected unindent. [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\filters\index.rst:181: ERROR: Malformed table. -Right border not aligned or missing. - -+-----------------+-----------------------------+-------------------------------------+ -| Pattern form | Example | Compiles to | -+=================+=============================+=====================================+ -| ``val%`` | ``like("Contoso%")`` | ``startswith(column,'Contoso')`` | -+-----------------+-----------------------------+-------------------------------------+ -| ``%val`` | ``like("%Ltd")`` | ``endswith(column,'Ltd')`` | -+-----------------+-----------------------------+-------------------------------------+ -| ``%val%`` | ``like("%Corp%")`` | ``contains(column,'Corp')`` | -+-----------------+-----------------------------+-------------------------------------+ -| No wildcard | ``like("Contoso")`` | ``column eq 'Contoso'`` | -+-----------------+-----------------------------+-------------------------------------+ -| Other | ``like("Con%oso")`` | :class:`ValueError` | -+-----------------+-----------------------------+-------------------------------------+ [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\query\index.rst:114: WARNING: Inline emphasis start-string without end-string. [docutils] -looking for now-outdated files... none found -pickling environment... done -checking consistency... done -preparing documents... done -copying assets...  -copying static files...  -Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\basic.css -Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\documentation_options.js -Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\language_data.js -Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\alabaster.css -copying static files: done -copying extra files...  -copying extra files: done -copying assets: done -writing output... [ 3%] autoapi/PowerPlatform/Dataverse/claude_skill/index writing output... [ 6%] autoapi/PowerPlatform/Dataverse/client/index writing output... [ 9%] autoapi/PowerPlatform/Dataverse/common/constants/index writing output... [ 11%] autoapi/PowerPlatform/Dataverse/common/index writing output... [ 14%] autoapi/PowerPlatform/Dataverse/core/config/index writing output... [ 17%] autoapi/PowerPlatform/Dataverse/core/errors/index writing output... [ 20%] autoapi/PowerPlatform/Dataverse/core/index writing output... [ 23%] autoapi/PowerPlatform/Dataverse/core/log_config/index writing output... [ 26%] autoapi/PowerPlatform/Dataverse/data/index writing output... [ 29%] autoapi/PowerPlatform/Dataverse/extensions/index writing output... [ 31%] autoapi/PowerPlatform/Dataverse/index writing output... [ 34%] autoapi/PowerPlatform/Dataverse/migration/index writing output... [ 37%] autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index writing output... [ 40%] autoapi/PowerPlatform/Dataverse/models/batch/index writing output... [ 43%] autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index writing output... [ 46%] autoapi/PowerPlatform/Dataverse/models/filters/index writing output... [ 49%] autoapi/PowerPlatform/Dataverse/models/index writing output... [ 51%] autoapi/PowerPlatform/Dataverse/models/labels/index writing output... [ 54%] autoapi/PowerPlatform/Dataverse/models/protocol/index writing output... [ 57%] autoapi/PowerPlatform/Dataverse/models/query_builder/index writing output... [ 60%] autoapi/PowerPlatform/Dataverse/models/record/index writing output... [ 63%] autoapi/PowerPlatform/Dataverse/models/relationship/index writing output... [ 66%] autoapi/PowerPlatform/Dataverse/models/table_info/index writing output... [ 69%] autoapi/PowerPlatform/Dataverse/models/upsert/index writing output... [ 71%] autoapi/PowerPlatform/Dataverse/operations/batch/index writing output... [ 74%] autoapi/PowerPlatform/Dataverse/operations/dataframe/index writing output... [ 77%] autoapi/PowerPlatform/Dataverse/operations/files/index writing output... [ 80%] autoapi/PowerPlatform/Dataverse/operations/index writing output... [ 83%] autoapi/PowerPlatform/Dataverse/operations/query/index writing output... [ 86%] autoapi/PowerPlatform/Dataverse/operations/records/index writing output... [ 89%] autoapi/PowerPlatform/Dataverse/operations/tables/index writing output... [ 91%] autoapi/PowerPlatform/Dataverse/utils/index writing output... [ 94%] autoapi/PowerPlatform/index writing output... [ 97%] autoapi/index writing output... [100%] index -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\client\index.rst:18: WARNING: py:class reference target not found: azure.core.credentials.TokenCredential [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\client\index.rst:16: WARNING: py:class reference target not found: azure.core.credentials.TokenCredential [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:33::1: WARNING: py:class reference target not found: Record [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:42::1: WARNING: py:class reference target not found: ColumnProxy [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:48: WARNING: py:class reference target not found: ColumnProxy [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:61: WARNING: py:class reference target not found: ColumnProxy [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:73: WARNING: py:class reference target not found: FilterExpression [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:154: WARNING: py:class reference target not found: Record [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:162: WARNING: py:class reference target not found: Record [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:44: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:113: WARNING: py:class reference target not found: pathlib.Path [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:193: WARNING: py:obj reference target not found: _QueryBuilderBase [ref.obj] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:295: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\record\index.rst:22: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:30: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:93: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:114: WARNING: py:class reference target not found: _BatchContext [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:324: WARNING: py:class reference target not found: _BatchContext [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:508: WARNING: py:class reference target not found: _BatchContext [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:539: WARNING: py:class reference target not found: _BatchContext [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:563: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:585: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:617: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:54: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:93: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:154: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:154: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:189: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:248: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:: WARNING: py:class reference target not found: collections.abc.Iterable [ref.class] -generating indices... genindex py-modindex done -writing additional pages... search done -dumping search index in English (code: en)... done -dumping object inventory... done -build succeeded, 48 warnings. - -The HTML pages are in docs_local\_build. diff --git a/docs_local/build_no_imported.log b/docs_local/build_no_imported.log deleted file mode 100644 index e3b19bf7..00000000 --- a/docs_local/build_no_imported.log +++ /dev/null @@ -1,120 +0,0 @@ -Running Sphinx v9.1.0 -loading translations [en]... done -making output directory... done -[AutoAPI] Reading files... [ 2%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\__init__.py [AutoAPI] Reading files... [ 4%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\client.py [AutoAPI] Reading files... [ 7%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\_skill_installer.py [AutoAPI] Reading files... [ 9%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\__init__.py [AutoAPI] Reading files... [ 11%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\claude_skill\__init__.py [AutoAPI] Reading files... [ 13%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\constants.py [AutoAPI] Reading files... [ 15%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\__init__.py [AutoAPI] Reading files... [ 17%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\config.py [AutoAPI] Reading files... [ 20%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\errors.py [AutoAPI] Reading files... [ 22%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\log_config.py [AutoAPI] Reading files... [ 24%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_auth.py [AutoAPI] Reading files... [ 26%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_error_codes.py [AutoAPI] Reading files... [ 28%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http.py [AutoAPI] Reading files... [ 30%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http_logger.py [AutoAPI] Reading files... [ 33%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\__init__.py [AutoAPI] Reading files... [ 35%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch.py [AutoAPI] Reading files... [ 37%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch_base.py [AutoAPI] Reading files... [ 39%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata.py [AutoAPI] Reading files... [ 41%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata_base.py [AutoAPI] Reading files... [ 43%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_raw_request.py [AutoAPI] Reading files... [ 46%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_relationships.py [AutoAPI] Reading files... [ 48%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_upload.py [AutoAPI] Reading files... [ 50%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\__init__.py [AutoAPI] Reading files... [ 52%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\extensions\__init__.py [AutoAPI] Reading files... [ 54%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\migrate_v0_to_v1.py [AutoAPI] Reading files... [ 57%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\__init__.py [AutoAPI] Reading files... [ 59%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\batch.py [AutoAPI] Reading files... [ 61%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\fetchxml_query.py [AutoAPI] Reading files... [ 63%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\filters.py [AutoAPI] Reading files... [ 65%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\labels.py [AutoAPI] Reading files... [ 67%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\protocol.py [AutoAPI] Reading files... [ 70%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\query_builder.py [AutoAPI] Reading files... [ 72%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\record.py [AutoAPI] Reading files... [ 74%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\relationship.py [AutoAPI] Reading files... [ 76%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\table_info.py [AutoAPI] Reading files... [ 78%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\upsert.py [AutoAPI] Reading files... [ 80%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\__init__.py [AutoAPI] Reading files... [ 83%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\batch.py [AutoAPI] Reading files... [ 85%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\dataframe.py [AutoAPI] Reading files... [ 87%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\files.py [AutoAPI] Reading files... [ 89%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\query.py [AutoAPI] Reading files... [ 91%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\records.py [AutoAPI] Reading files... [ 93%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\tables.py [AutoAPI] Reading files... [ 96%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\__init__.py [AutoAPI] Reading files... [ 98%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\_pandas.py [AutoAPI] Reading files... [100%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\__init__.py -WARNING: Cannot resolve cyclic import: PowerPlatform.Dataverse.client, PowerPlatform.Dataverse.data._odata, PowerPlatform.Dataverse.data._odata_base, PowerPlatform.Dataverse, PowerPlatform.Dataverse.client [autoapi.python_import_resolution] -[AutoAPI] Mapping Data... [ 2%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\__init__.py [AutoAPI] Mapping Data... [ 4%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\client.py [AutoAPI] Mapping Data... [ 7%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\_skill_installer.py [AutoAPI] Mapping Data... [ 9%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\__init__.py [AutoAPI] Mapping Data... [ 11%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\claude_skill\__init__.py [AutoAPI] Mapping Data... [ 13%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\constants.py [AutoAPI] Mapping Data... [ 15%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\__init__.py [AutoAPI] Mapping Data... [ 17%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\config.py [AutoAPI] Mapping Data... [ 20%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\errors.py [AutoAPI] Mapping Data... [ 22%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\log_config.py [AutoAPI] Mapping Data... [ 24%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_auth.py [AutoAPI] Mapping Data... [ 26%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_error_codes.py [AutoAPI] Mapping Data... [ 28%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http.py [AutoAPI] Mapping Data... [ 30%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http_logger.py [AutoAPI] Mapping Data... [ 33%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\__init__.py [AutoAPI] Mapping Data... [ 35%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch.py [AutoAPI] Mapping Data... [ 37%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch_base.py [AutoAPI] Mapping Data... [ 39%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata.py [AutoAPI] Mapping Data... [ 41%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata_base.py [AutoAPI] Mapping Data... [ 43%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_raw_request.py [AutoAPI] Mapping Data... [ 46%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_relationships.py [AutoAPI] Mapping Data... [ 48%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_upload.py [AutoAPI] Mapping Data... [ 50%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\__init__.py [AutoAPI] Mapping Data... [ 52%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\extensions\__init__.py [AutoAPI] Mapping Data... [ 54%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\migrate_v0_to_v1.py [AutoAPI] Mapping Data... [ 57%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\__init__.py [AutoAPI] Mapping Data... [ 59%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\batch.py [AutoAPI] Mapping Data... [ 61%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\fetchxml_query.py [AutoAPI] Mapping Data... [ 63%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\filters.py [AutoAPI] Mapping Data... [ 65%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\labels.py [AutoAPI] Mapping Data... [ 67%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\protocol.py [AutoAPI] Mapping Data... [ 70%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\query_builder.py [AutoAPI] Mapping Data... [ 72%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\record.py [AutoAPI] Mapping Data... [ 74%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\relationship.py [AutoAPI] Mapping Data... [ 76%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\table_info.py [AutoAPI] Mapping Data... [ 78%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\upsert.py [AutoAPI] Mapping Data... [ 80%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\__init__.py [AutoAPI] Mapping Data... [ 83%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\batch.py [AutoAPI] Mapping Data... [ 85%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\dataframe.py [AutoAPI] Mapping Data... [ 87%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\files.py [AutoAPI] Mapping Data... [ 89%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\query.py [AutoAPI] Mapping Data... [ 91%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\records.py [AutoAPI] Mapping Data... [ 93%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\tables.py [AutoAPI] Mapping Data... [ 96%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\__init__.py [AutoAPI] Mapping Data... [ 98%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\_pandas.py [AutoAPI] Mapping Data... [100%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\__init__.py -[AutoAPI] Rendering Data... [ 3%] PowerPlatform -[AutoAPI] Rendering Data... [ 6%] PowerPlatform.Dataverse -[AutoAPI] Rendering Data... [ 9%] PowerPlatform.Dataverse.core -[AutoAPI] Rendering Data... [ 12%] PowerPlatform.Dataverse.data -[AutoAPI] Rendering Data... [ 15%] PowerPlatform.Dataverse.utils -[AutoAPI] Rendering Data... [ 18%] PowerPlatform.Dataverse.client -[AutoAPI] Rendering Data... [ 21%] PowerPlatform.Dataverse.common -[AutoAPI] Rendering Data... [ 24%] PowerPlatform.Dataverse.models -[AutoAPI] Rendering Data... [ 27%] PowerPlatform.Dataverse.migration -[AutoAPI] Rendering Data... [ 30%] PowerPlatform.Dataverse.extensions -[AutoAPI] Rendering Data... [ 33%] PowerPlatform.Dataverse.operations -[AutoAPI] Rendering Data... [ 36%] PowerPlatform.Dataverse.core.config -[AutoAPI] Rendering Data... [ 39%] PowerPlatform.Dataverse.core.errors -[AutoAPI] Rendering Data... [ 42%] PowerPlatform.Dataverse.claude_skill -[AutoAPI] Rendering Data... [ 45%] PowerPlatform.Dataverse.models.batch -[AutoAPI] Rendering Data... [ 48%] PowerPlatform.Dataverse.models.labels -[AutoAPI] Rendering Data... [ 52%] PowerPlatform.Dataverse.models.record -[AutoAPI] Rendering Data... [ 55%] PowerPlatform.Dataverse.models.upsert -[AutoAPI] Rendering Data... [ 58%] PowerPlatform.Dataverse.models.filters -[AutoAPI] Rendering Data... [ 61%] PowerPlatform.Dataverse.core.log_config -[AutoAPI] Rendering Data... [ 64%] PowerPlatform.Dataverse.models.protocol -[AutoAPI] Rendering Data... [ 67%] PowerPlatform.Dataverse.common.constants -[AutoAPI] Rendering Data... [ 70%] PowerPlatform.Dataverse.operations.batch -[AutoAPI] Rendering Data... [ 73%] PowerPlatform.Dataverse.operations.files -[AutoAPI] Rendering Data... [ 76%] PowerPlatform.Dataverse.operations.query -[AutoAPI] Rendering Data... [ 79%] PowerPlatform.Dataverse.models.table_info -[AutoAPI] Rendering Data... [ 82%] PowerPlatform.Dataverse.operations.tables -[AutoAPI] Rendering Data... [ 85%] PowerPlatform.Dataverse.operations.records -[AutoAPI] Rendering Data... [ 88%] PowerPlatform.Dataverse.models.relationship -[AutoAPI] Rendering Data... [ 91%] PowerPlatform.Dataverse.models.query_builder -[AutoAPI] Rendering Data... [ 94%] PowerPlatform.Dataverse.operations.dataframe -[AutoAPI] Rendering Data... [ 97%] PowerPlatform.Dataverse.models.fetchxml_query -[AutoAPI] Rendering Data... [100%] PowerPlatform.Dataverse.migration.migrate_v0_to_v1 - -[autosummary] generating autosummary for: index.rst -building [mo]: targets for 0 po files that are out of date -writing output...  -building [html]: targets for 1 source files that are out of date -updating environment: [new config] 35 added, 0 changed, 0 removed -reading sources... [ 3%] autoapi/PowerPlatform/Dataverse/claude_skill/index reading sources... [ 6%] autoapi/PowerPlatform/Dataverse/client/index reading sources... [ 9%] autoapi/PowerPlatform/Dataverse/common/constants/index reading sources... [ 11%] autoapi/PowerPlatform/Dataverse/common/index reading sources... [ 14%] autoapi/PowerPlatform/Dataverse/core/config/index reading sources... [ 17%] autoapi/PowerPlatform/Dataverse/core/errors/index reading sources... [ 20%] autoapi/PowerPlatform/Dataverse/core/index reading sources... [ 23%] autoapi/PowerPlatform/Dataverse/core/log_config/index reading sources... [ 26%] autoapi/PowerPlatform/Dataverse/data/index reading sources... [ 29%] autoapi/PowerPlatform/Dataverse/extensions/index reading sources... [ 31%] autoapi/PowerPlatform/Dataverse/index reading sources... [ 34%] autoapi/PowerPlatform/Dataverse/migration/index reading sources... [ 37%] autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index reading sources... [ 40%] autoapi/PowerPlatform/Dataverse/models/batch/index reading sources... [ 43%] autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index reading sources... [ 46%] autoapi/PowerPlatform/Dataverse/models/filters/index reading sources... [ 49%] autoapi/PowerPlatform/Dataverse/models/index reading sources... [ 51%] autoapi/PowerPlatform/Dataverse/models/labels/index reading sources... [ 54%] autoapi/PowerPlatform/Dataverse/models/protocol/index reading sources... [ 57%] autoapi/PowerPlatform/Dataverse/models/query_builder/index reading sources... [ 60%] autoapi/PowerPlatform/Dataverse/models/record/index reading sources... [ 63%] autoapi/PowerPlatform/Dataverse/models/relationship/index reading sources... [ 66%] autoapi/PowerPlatform/Dataverse/models/table_info/index reading sources... [ 69%] autoapi/PowerPlatform/Dataverse/models/upsert/index reading sources... [ 71%] autoapi/PowerPlatform/Dataverse/operations/batch/index reading sources... [ 74%] autoapi/PowerPlatform/Dataverse/operations/dataframe/index reading sources... [ 77%] autoapi/PowerPlatform/Dataverse/operations/files/index reading sources... [ 80%] autoapi/PowerPlatform/Dataverse/operations/index reading sources... [ 83%] autoapi/PowerPlatform/Dataverse/operations/query/index reading sources... [ 86%] autoapi/PowerPlatform/Dataverse/operations/records/index reading sources... [ 89%] autoapi/PowerPlatform/Dataverse/operations/tables/index reading sources... [ 91%] autoapi/PowerPlatform/Dataverse/utils/index reading sources... [ 94%] autoapi/PowerPlatform/index reading sources... [ 97%] autoapi/index reading sources... [100%] index -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:77: ERROR: Unexpected indentation. [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:72: WARNING: Block quote ends without a blank line; unexpected unindent. [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:78: WARNING: Definition list ends without a blank line; unexpected unindent. [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\filters\index.rst:181: ERROR: Malformed table. -Right border not aligned or missing. - -+-----------------+-----------------------------+-------------------------------------+ -| Pattern form | Example | Compiles to | -+=================+=============================+=====================================+ -| ``val%`` | ``like("Contoso%")`` | ``startswith(column,'Contoso')`` | -+-----------------+-----------------------------+-------------------------------------+ -| ``%val`` | ``like("%Ltd")`` | ``endswith(column,'Ltd')`` | -+-----------------+-----------------------------+-------------------------------------+ -| ``%val%`` | ``like("%Corp%")`` | ``contains(column,'Corp')`` | -+-----------------+-----------------------------+-------------------------------------+ -| No wildcard | ``like("Contoso")`` | ``column eq 'Contoso'`` | -+-----------------+-----------------------------+-------------------------------------+ -| Other | ``like("Con%oso")`` | :class:`ValueError` | -+-----------------+-----------------------------+-------------------------------------+ [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\query\index.rst:114: WARNING: Inline emphasis start-string without end-string. [docutils] -looking for now-outdated files... none found -pickling environment... done -checking consistency... done -preparing documents... done -copying assets...  -copying static files...  -Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\basic.css -Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\documentation_options.js -Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\language_data.js -Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\alabaster.css -copying static files: done -copying extra files...  -copying extra files: done -copying assets: done -writing output... [ 3%] autoapi/PowerPlatform/Dataverse/claude_skill/index writing output... [ 6%] autoapi/PowerPlatform/Dataverse/client/index writing output... [ 9%] autoapi/PowerPlatform/Dataverse/common/constants/index writing output... [ 11%] autoapi/PowerPlatform/Dataverse/common/index writing output... [ 14%] autoapi/PowerPlatform/Dataverse/core/config/index writing output... [ 17%] autoapi/PowerPlatform/Dataverse/core/errors/index writing output... [ 20%] autoapi/PowerPlatform/Dataverse/core/index writing output... [ 23%] autoapi/PowerPlatform/Dataverse/core/log_config/index writing output... [ 26%] autoapi/PowerPlatform/Dataverse/data/index writing output... [ 29%] autoapi/PowerPlatform/Dataverse/extensions/index writing output... [ 31%] autoapi/PowerPlatform/Dataverse/index writing output... [ 34%] autoapi/PowerPlatform/Dataverse/migration/index writing output... [ 37%] autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index writing output... [ 40%] autoapi/PowerPlatform/Dataverse/models/batch/index writing output... [ 43%] autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index writing output... [ 46%] autoapi/PowerPlatform/Dataverse/models/filters/index writing output... [ 49%] autoapi/PowerPlatform/Dataverse/models/index writing output... [ 51%] autoapi/PowerPlatform/Dataverse/models/labels/index writing output... [ 54%] autoapi/PowerPlatform/Dataverse/models/protocol/index writing output... [ 57%] autoapi/PowerPlatform/Dataverse/models/query_builder/index writing output... [ 60%] autoapi/PowerPlatform/Dataverse/models/record/index writing output... [ 63%] autoapi/PowerPlatform/Dataverse/models/relationship/index writing output... [ 66%] autoapi/PowerPlatform/Dataverse/models/table_info/index writing output... [ 69%] autoapi/PowerPlatform/Dataverse/models/upsert/index writing output... [ 71%] autoapi/PowerPlatform/Dataverse/operations/batch/index writing output... [ 74%] autoapi/PowerPlatform/Dataverse/operations/dataframe/index writing output... [ 77%] autoapi/PowerPlatform/Dataverse/operations/files/index writing output... [ 80%] autoapi/PowerPlatform/Dataverse/operations/index writing output... [ 83%] autoapi/PowerPlatform/Dataverse/operations/query/index writing output... [ 86%] autoapi/PowerPlatform/Dataverse/operations/records/index writing output... [ 89%] autoapi/PowerPlatform/Dataverse/operations/tables/index writing output... [ 91%] autoapi/PowerPlatform/Dataverse/utils/index writing output... [ 94%] autoapi/PowerPlatform/index writing output... [ 97%] autoapi/index writing output... [100%] index -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\client\index.rst:18: WARNING: py:class reference target not found: azure.core.credentials.TokenCredential [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\client\index.rst:16: WARNING: py:class reference target not found: azure.core.credentials.TokenCredential [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:113: WARNING: py:class reference target not found: pathlib.Path [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:193: WARNING: py:obj reference target not found: _QueryBuilderBase [ref.obj] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:295: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\record\index.rst:22: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:30: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:93: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:114: WARNING: py:class reference target not found: _BatchContext [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:324: WARNING: py:class reference target not found: _BatchContext [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:508: WARNING: py:class reference target not found: _BatchContext [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:539: WARNING: py:class reference target not found: _BatchContext [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:563: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:585: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:617: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:54: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:93: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:154: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:154: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:189: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:248: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:: WARNING: py:class reference target not found: collections.abc.Iterable [ref.class] -generating indices... genindex py-modindex done -writing additional pages... search done -dumping search index in English (code: en)... done -dumping object inventory... done -build succeeded, 38 warnings. - -The HTML pages are in docs_local\_build. diff --git a/docs_local/build_option1.log b/docs_local/build_option1.log deleted file mode 100644 index fba56aac..00000000 --- a/docs_local/build_option1.log +++ /dev/null @@ -1,140 +0,0 @@ -Running Sphinx v9.1.0 -loading translations [en]... done -making output directory... done -[AutoAPI] Reading files... [ 2%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\__init__.py [AutoAPI] Reading files... [ 5%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\client.py [AutoAPI] Reading files... [ 7%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\_skill_installer.py [AutoAPI] Reading files... [ 9%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\__init__.py [AutoAPI] Reading files... [ 11%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\claude_skill\__init__.py [AutoAPI] Reading files... [ 14%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\constants.py [AutoAPI] Reading files... [ 16%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\__init__.py [AutoAPI] Reading files... [ 18%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\config.py [AutoAPI] Reading files... [ 20%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\errors.py [AutoAPI] Reading files... [ 23%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\log_config.py [AutoAPI] Reading files... [ 25%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_auth.py [AutoAPI] Reading files... [ 27%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_error_codes.py [AutoAPI] Reading files... [ 30%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http.py [AutoAPI] Reading files... [ 32%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http_logger.py [AutoAPI] Reading files... [ 34%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\__init__.py [AutoAPI] Reading files... [ 36%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch.py [AutoAPI] Reading files... [ 39%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata.py [AutoAPI] Reading files... [ 41%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_raw_request.py [AutoAPI] Reading files... [ 43%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_relationships.py [AutoAPI] Reading files... [ 45%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_upload.py [AutoAPI] Reading files... [ 48%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\__init__.py [AutoAPI] Reading files... [ 50%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\extensions\__init__.py [AutoAPI] Reading files... [ 52%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\migrate_v0_to_v1.py [AutoAPI] Reading files... [ 55%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\__init__.py [AutoAPI] Reading files... [ 57%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\batch.py [AutoAPI] Reading files... [ 59%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\fetchxml_query.py [AutoAPI] Reading files... [ 61%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\filters.py [AutoAPI] Reading files... [ 64%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\labels.py [AutoAPI] Reading files... [ 66%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\protocol.py [AutoAPI] Reading files... [ 68%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\query_builder.py [AutoAPI] Reading files... [ 70%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\record.py [AutoAPI] Reading files... [ 73%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\relationship.py [AutoAPI] Reading files... [ 75%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\table_info.py [AutoAPI] Reading files... [ 77%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\upsert.py [AutoAPI] Reading files... [ 80%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\__init__.py [AutoAPI] Reading files... [ 82%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\batch.py [AutoAPI] Reading files... [ 84%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\dataframe.py [AutoAPI] Reading files... [ 86%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\files.py [AutoAPI] Reading files... [ 89%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\query.py [AutoAPI] Reading files... [ 91%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\records.py [AutoAPI] Reading files... [ 93%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\tables.py [AutoAPI] Reading files... [ 95%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\__init__.py [AutoAPI] Reading files... [ 98%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\_pandas.py [AutoAPI] Reading files... [100%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\__init__.py -[AutoAPI] Mapping Data... [ 2%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\__init__.py [AutoAPI] Mapping Data... [ 5%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\client.py [AutoAPI] Mapping Data... [ 7%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\_skill_installer.py [AutoAPI] Mapping Data... [ 9%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\__init__.py [AutoAPI] Mapping Data... [ 11%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\claude_skill\__init__.py [AutoAPI] Mapping Data... [ 14%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\constants.py [AutoAPI] Mapping Data... [ 16%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\__init__.py [AutoAPI] Mapping Data... [ 18%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\config.py [AutoAPI] Mapping Data... [ 20%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\errors.py [AutoAPI] Mapping Data... [ 23%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\log_config.py [AutoAPI] Mapping Data... [ 25%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_auth.py [AutoAPI] Mapping Data... [ 27%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_error_codes.py [AutoAPI] Mapping Data... [ 30%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http.py [AutoAPI] Mapping Data... [ 32%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http_logger.py [AutoAPI] Mapping Data... [ 34%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\__init__.py [AutoAPI] Mapping Data... [ 36%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch.py [AutoAPI] Mapping Data... [ 39%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata.py [AutoAPI] Mapping Data... [ 41%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_raw_request.py [AutoAPI] Mapping Data... [ 43%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_relationships.py [AutoAPI] Mapping Data... [ 45%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_upload.py [AutoAPI] Mapping Data... [ 48%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\__init__.py [AutoAPI] Mapping Data... [ 50%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\extensions\__init__.py [AutoAPI] Mapping Data... [ 52%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\migrate_v0_to_v1.py [AutoAPI] Mapping Data... [ 55%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\__init__.py [AutoAPI] Mapping Data... [ 57%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\batch.py [AutoAPI] Mapping Data... [ 59%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\fetchxml_query.py [AutoAPI] Mapping Data... [ 61%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\filters.py [AutoAPI] Mapping Data... [ 64%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\labels.py [AutoAPI] Mapping Data... [ 66%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\protocol.py [AutoAPI] Mapping Data... [ 68%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\query_builder.py [AutoAPI] Mapping Data... [ 70%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\record.py [AutoAPI] Mapping Data... [ 73%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\relationship.py [AutoAPI] Mapping Data... [ 75%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\table_info.py [AutoAPI] Mapping Data... [ 77%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\upsert.py [AutoAPI] Mapping Data... [ 80%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\__init__.py [AutoAPI] Mapping Data... [ 82%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\batch.py [AutoAPI] Mapping Data... [ 84%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\dataframe.py [AutoAPI] Mapping Data... [ 86%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\files.py [AutoAPI] Mapping Data... [ 89%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\query.py [AutoAPI] Mapping Data... [ 91%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\records.py [AutoAPI] Mapping Data... [ 93%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\tables.py [AutoAPI] Mapping Data... [ 95%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\__init__.py [AutoAPI] Mapping Data... [ 98%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\_pandas.py [AutoAPI] Mapping Data... [100%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\__init__.py -[AutoAPI] Rendering Data... [ 3%] PowerPlatform -[AutoAPI] Rendering Data... [ 6%] PowerPlatform.Dataverse -[AutoAPI] Rendering Data... [ 9%] PowerPlatform.Dataverse.core -[AutoAPI] Rendering Data... [ 12%] PowerPlatform.Dataverse.data -[AutoAPI] Rendering Data... [ 15%] PowerPlatform.Dataverse.utils -[AutoAPI] Rendering Data... [ 18%] PowerPlatform.Dataverse.client -[AutoAPI] Rendering Data... [ 21%] PowerPlatform.Dataverse.common -[AutoAPI] Rendering Data... [ 24%] PowerPlatform.Dataverse.models -[AutoAPI] Rendering Data... [ 27%] PowerPlatform.Dataverse.migration -[AutoAPI] Rendering Data... [ 30%] PowerPlatform.Dataverse.extensions -[AutoAPI] Rendering Data... [ 33%] PowerPlatform.Dataverse.operations -[AutoAPI] Rendering Data... [ 36%] PowerPlatform.Dataverse.core.config -[AutoAPI] Rendering Data... [ 39%] PowerPlatform.Dataverse.core.errors -[AutoAPI] Rendering Data... [ 42%] PowerPlatform.Dataverse.claude_skill -[AutoAPI] Rendering Data... [ 45%] PowerPlatform.Dataverse.models.batch -[AutoAPI] Rendering Data... [ 48%] PowerPlatform.Dataverse.models.labels -[AutoAPI] Rendering Data... [ 52%] PowerPlatform.Dataverse.models.record -[AutoAPI] Rendering Data... [ 55%] PowerPlatform.Dataverse.models.upsert -[AutoAPI] Rendering Data... [ 58%] PowerPlatform.Dataverse.models.filters -[AutoAPI] Rendering Data... [ 61%] PowerPlatform.Dataverse.core.log_config -[AutoAPI] Rendering Data... [ 64%] PowerPlatform.Dataverse.models.protocol -[AutoAPI] Rendering Data... [ 67%] PowerPlatform.Dataverse.common.constants -[AutoAPI] Rendering Data... [ 70%] PowerPlatform.Dataverse.operations.batch -[AutoAPI] Rendering Data... [ 73%] PowerPlatform.Dataverse.operations.files -[AutoAPI] Rendering Data... [ 76%] PowerPlatform.Dataverse.operations.query -[AutoAPI] Rendering Data... [ 79%] PowerPlatform.Dataverse.models.table_info -[AutoAPI] Rendering Data... [ 82%] PowerPlatform.Dataverse.operations.tables -[AutoAPI] Rendering Data... [ 85%] PowerPlatform.Dataverse.operations.records -[AutoAPI] Rendering Data... [ 88%] PowerPlatform.Dataverse.models.relationship -[AutoAPI] Rendering Data... [ 91%] PowerPlatform.Dataverse.models.query_builder -[AutoAPI] Rendering Data... [ 94%] PowerPlatform.Dataverse.operations.dataframe -[AutoAPI] Rendering Data... [ 97%] PowerPlatform.Dataverse.models.fetchxml_query -[AutoAPI] Rendering Data... [100%] PowerPlatform.Dataverse.migration.migrate_v0_to_v1 - -[autosummary] generating autosummary for: index.rst -building [mo]: targets for 0 po files that are out of date -writing output...  -building [html]: targets for 1 source files that are out of date -updating environment: [new config] 35 added, 0 changed, 0 removed -reading sources... [ 3%] autoapi/PowerPlatform/Dataverse/claude_skill/index reading sources... [ 6%] autoapi/PowerPlatform/Dataverse/client/index reading sources... [ 9%] autoapi/PowerPlatform/Dataverse/common/constants/index reading sources... [ 11%] autoapi/PowerPlatform/Dataverse/common/index reading sources... [ 14%] autoapi/PowerPlatform/Dataverse/core/config/index reading sources... [ 17%] autoapi/PowerPlatform/Dataverse/core/errors/index reading sources... [ 20%] autoapi/PowerPlatform/Dataverse/core/index reading sources... [ 23%] autoapi/PowerPlatform/Dataverse/core/log_config/index reading sources... [ 26%] autoapi/PowerPlatform/Dataverse/data/index reading sources... [ 29%] autoapi/PowerPlatform/Dataverse/extensions/index reading sources... [ 31%] autoapi/PowerPlatform/Dataverse/index reading sources... [ 34%] autoapi/PowerPlatform/Dataverse/migration/index reading sources... [ 37%] autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index reading sources... [ 40%] autoapi/PowerPlatform/Dataverse/models/batch/index reading sources... [ 43%] autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index reading sources... [ 46%] autoapi/PowerPlatform/Dataverse/models/filters/index reading sources... [ 49%] autoapi/PowerPlatform/Dataverse/models/index reading sources... [ 51%] autoapi/PowerPlatform/Dataverse/models/labels/index reading sources... [ 54%] autoapi/PowerPlatform/Dataverse/models/protocol/index reading sources... [ 57%] autoapi/PowerPlatform/Dataverse/models/query_builder/index reading sources... [ 60%] autoapi/PowerPlatform/Dataverse/models/record/index reading sources... [ 63%] autoapi/PowerPlatform/Dataverse/models/relationship/index reading sources... [ 66%] autoapi/PowerPlatform/Dataverse/models/table_info/index reading sources... [ 69%] autoapi/PowerPlatform/Dataverse/models/upsert/index reading sources... [ 71%] autoapi/PowerPlatform/Dataverse/operations/batch/index reading sources... [ 74%] autoapi/PowerPlatform/Dataverse/operations/dataframe/index reading sources... [ 77%] autoapi/PowerPlatform/Dataverse/operations/files/index reading sources... [ 80%] autoapi/PowerPlatform/Dataverse/operations/index reading sources... [ 83%] autoapi/PowerPlatform/Dataverse/operations/query/index reading sources... [ 86%] autoapi/PowerPlatform/Dataverse/operations/records/index reading sources... [ 89%] autoapi/PowerPlatform/Dataverse/operations/tables/index reading sources... [ 91%] autoapi/PowerPlatform/Dataverse/utils/index reading sources... [ 94%] autoapi/PowerPlatform/index reading sources... [ 97%] autoapi/index reading sources... [100%] index -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:77: ERROR: Unexpected indentation. [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:72: WARNING: Block quote ends without a blank line; unexpected unindent. [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:78: WARNING: Definition list ends without a blank line; unexpected unindent. [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\filters\index.rst:181: ERROR: Malformed table. -Right border not aligned or missing. - -+-----------------+-----------------------------+-------------------------------------+ -| Pattern form | Example | Compiles to | -+=================+=============================+=====================================+ -| ``val%`` | ``like("Contoso%")`` | ``startswith(column,'Contoso')`` | -+-----------------+-----------------------------+-------------------------------------+ -| ``%val`` | ``like("%Ltd")`` | ``endswith(column,'Ltd')`` | -+-----------------+-----------------------------+-------------------------------------+ -| ``%val%`` | ``like("%Corp%")`` | ``contains(column,'Corp')`` | -+-----------------+-----------------------------+-------------------------------------+ -| No wildcard | ``like("Contoso")`` | ``column eq 'Contoso'`` | -+-----------------+-----------------------------+-------------------------------------+ -| Other | ``like("Con%oso")`` | :class:`ValueError` | -+-----------------+-----------------------------+-------------------------------------+ [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\query\index.rst:114: WARNING: Inline emphasis start-string without end-string. [docutils] -looking for now-outdated files... none found -pickling environment... done -checking consistency... done -preparing documents... done -copying assets...  -copying static files...  -Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\basic.css -Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\documentation_options.js -Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\language_data.js -Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\alabaster.css -copying static files: done -copying extra files...  -copying extra files: done -copying assets: done -writing output... [ 3%] autoapi/PowerPlatform/Dataverse/claude_skill/index writing output... [ 6%] autoapi/PowerPlatform/Dataverse/client/index writing output... [ 9%] autoapi/PowerPlatform/Dataverse/common/constants/index writing output... [ 11%] autoapi/PowerPlatform/Dataverse/common/index writing output... [ 14%] autoapi/PowerPlatform/Dataverse/core/config/index writing output... [ 17%] autoapi/PowerPlatform/Dataverse/core/errors/index writing output... [ 20%] autoapi/PowerPlatform/Dataverse/core/index writing output... [ 23%] autoapi/PowerPlatform/Dataverse/core/log_config/index writing output... [ 26%] autoapi/PowerPlatform/Dataverse/data/index writing output... [ 29%] autoapi/PowerPlatform/Dataverse/extensions/index writing output... [ 31%] autoapi/PowerPlatform/Dataverse/index writing output... [ 34%] autoapi/PowerPlatform/Dataverse/migration/index writing output... [ 37%] autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index writing output... [ 40%] autoapi/PowerPlatform/Dataverse/models/batch/index writing output... [ 43%] autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index writing output... [ 46%] autoapi/PowerPlatform/Dataverse/models/filters/index writing output... [ 49%] autoapi/PowerPlatform/Dataverse/models/index writing output... [ 51%] autoapi/PowerPlatform/Dataverse/models/labels/index writing output... [ 54%] autoapi/PowerPlatform/Dataverse/models/protocol/index writing output... [ 57%] autoapi/PowerPlatform/Dataverse/models/query_builder/index writing output... [ 60%] autoapi/PowerPlatform/Dataverse/models/record/index writing output... [ 63%] autoapi/PowerPlatform/Dataverse/models/relationship/index writing output... [ 66%] autoapi/PowerPlatform/Dataverse/models/table_info/index writing output... [ 69%] autoapi/PowerPlatform/Dataverse/models/upsert/index writing output... [ 71%] autoapi/PowerPlatform/Dataverse/operations/batch/index writing output... [ 74%] autoapi/PowerPlatform/Dataverse/operations/dataframe/index writing output... [ 77%] autoapi/PowerPlatform/Dataverse/operations/files/index writing output... [ 80%] autoapi/PowerPlatform/Dataverse/operations/index writing output... [ 83%] autoapi/PowerPlatform/Dataverse/operations/query/index writing output... [ 86%] autoapi/PowerPlatform/Dataverse/operations/records/index writing output... [ 89%] autoapi/PowerPlatform/Dataverse/operations/tables/index writing output... [ 91%] autoapi/PowerPlatform/Dataverse/utils/index writing output... [ 94%] autoapi/PowerPlatform/index writing output... [ 97%] autoapi/index writing output... [100%] index -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\client\index.rst:18: WARNING: py:class reference target not found: azure.core.credentials.TokenCredential [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\client\index.rst:22: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._odata._ODataClient [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\client\index.rst:16: WARNING: py:class reference target not found: azure.core.credentials.TokenCredential [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:33::1: WARNING: py:class reference target not found: Record [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:42::1: WARNING: py:class reference target not found: ColumnProxy [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:48: WARNING: py:class reference target not found: ColumnProxy [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:61: WARNING: py:class reference target not found: ColumnProxy [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:73: WARNING: py:class reference target not found: FilterExpression [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:154: WARNING: py:class reference target not found: Record [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:162: WARNING: py:class reference target not found: Record [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:44: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:113: WARNING: py:class reference target not found: pathlib.Path [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\fetchxml_query\index.rst:37: WARNING: py:class reference target not found: QueryResult [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\fetchxml_query\index.rst:55: WARNING: py:class reference target not found: QueryResult [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\fetchxml_query\index.rst:67: WARNING: py:class reference target not found: QueryResult [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:487: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\record\index.rst:22: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:30: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:93: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:290: WARNING: py:class reference target not found: FilterExpression [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:413: WARNING: py:attr reference target not found: BatchResult.responses [ref.attr] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:427: WARNING: py:attr reference target not found: BatchResult.responses [ref.attr] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:563: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:585: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:617: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:54: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:93: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:154: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:154: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:189: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:248: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:: WARNING: py:class reference target not found: collections.abc.Iterable [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:296: WARNING: py:class reference target not found: QueryResult [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:298: WARNING: py:class reference target not found: QueryResult [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:303: WARNING: py:class reference target not found: FilterExpression [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:320: WARNING: py:class reference target not found: QueryResult [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:342: WARNING: py:class reference target not found: QueryResult [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:350: WARNING: py:class reference target not found: FilterExpression [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:367: WARNING: py:class reference target not found: QueryResult [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\tables\index.rst:471: WARNING: py:attr reference target not found: AlternateKeyInfo.status [ref.attr] -generating indices... genindex py-modindex done -writing additional pages... search done -dumping search index in English (code: en)... done -dumping object inventory... done -build succeeded, 58 warnings. - -The HTML pages are in docs_local\_build. diff --git a/docs_local/build_optionA.log b/docs_local/build_optionA.log deleted file mode 100644 index b8e0129a..00000000 --- a/docs_local/build_optionA.log +++ /dev/null @@ -1,200 +0,0 @@ -Running Sphinx v9.1.0 -loading translations [en]... done -making output directory... done -[AutoAPI] Reading files... [ 2%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\__init__.py [AutoAPI] Reading files... [ 4%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\client.py [AutoAPI] Reading files... [ 7%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\_skill_installer.py [AutoAPI] Reading files... [ 9%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\__init__.py [AutoAPI] Reading files... [ 11%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\claude_skill\__init__.py [AutoAPI] Reading files... [ 13%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\constants.py [AutoAPI] Reading files... [ 15%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\__init__.py [AutoAPI] Reading files... [ 17%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\config.py [AutoAPI] Reading files... [ 20%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\errors.py [AutoAPI] Reading files... [ 22%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\log_config.py [AutoAPI] Reading files... [ 24%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_auth.py [AutoAPI] Reading files... [ 26%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_error_codes.py [AutoAPI] Reading files... [ 28%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http.py [AutoAPI] Reading files... [ 30%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http_logger.py [AutoAPI] Reading files... [ 33%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\__init__.py [AutoAPI] Reading files... [ 35%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch.py [AutoAPI] Reading files... [ 37%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch_base.py [AutoAPI] Reading files... [ 39%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata.py [AutoAPI] Reading files... [ 41%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata_base.py [AutoAPI] Reading files... [ 43%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_raw_request.py [AutoAPI] Reading files... [ 46%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_relationships.py [AutoAPI] Reading files... [ 48%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_upload.py [AutoAPI] Reading files... [ 50%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\__init__.py [AutoAPI] Reading files... [ 52%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\extensions\__init__.py [AutoAPI] Reading files... [ 54%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\migrate_v0_to_v1.py [AutoAPI] Reading files... [ 57%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\__init__.py [AutoAPI] Reading files... [ 59%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\batch.py [AutoAPI] Reading files... [ 61%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\fetchxml_query.py [AutoAPI] Reading files... [ 63%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\filters.py [AutoAPI] Reading files... [ 65%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\labels.py [AutoAPI] Reading files... [ 67%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\protocol.py [AutoAPI] Reading files... [ 70%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\query_builder.py [AutoAPI] Reading files... [ 72%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\record.py [AutoAPI] Reading files... [ 74%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\relationship.py [AutoAPI] Reading files... [ 76%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\table_info.py [AutoAPI] Reading files... [ 78%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\upsert.py [AutoAPI] Reading files... [ 80%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\__init__.py [AutoAPI] Reading files... [ 83%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\batch.py [AutoAPI] Reading files... [ 85%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\dataframe.py [AutoAPI] Reading files... [ 87%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\files.py [AutoAPI] Reading files... [ 89%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\query.py [AutoAPI] Reading files... [ 91%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\records.py [AutoAPI] Reading files... [ 93%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\tables.py [AutoAPI] Reading files... [ 96%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\__init__.py [AutoAPI] Reading files... [ 98%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\_pandas.py [AutoAPI] Reading files... [100%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\__init__.py -WARNING: Cannot resolve cyclic import: PowerPlatform.Dataverse.client, PowerPlatform.Dataverse.data._odata, PowerPlatform.Dataverse.data._odata_base, PowerPlatform.Dataverse, PowerPlatform.Dataverse.client [autoapi.python_import_resolution] -[AutoAPI] Mapping Data... [ 2%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\__init__.py [AutoAPI] Mapping Data... [ 4%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\client.py [AutoAPI] Mapping Data... [ 7%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\_skill_installer.py [AutoAPI] Mapping Data... [ 9%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\__init__.py [AutoAPI] Mapping Data... [ 11%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\claude_skill\__init__.py [AutoAPI] Mapping Data... [ 13%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\constants.py [AutoAPI] Mapping Data... [ 15%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\__init__.py [AutoAPI] Mapping Data... [ 17%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\config.py [AutoAPI] Mapping Data... [ 20%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\errors.py [AutoAPI] Mapping Data... [ 22%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\log_config.py [AutoAPI] Mapping Data... [ 24%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_auth.py [AutoAPI] Mapping Data... [ 26%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_error_codes.py [AutoAPI] Mapping Data... [ 28%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http.py [AutoAPI] Mapping Data... [ 30%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http_logger.py [AutoAPI] Mapping Data... [ 33%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\__init__.py [AutoAPI] Mapping Data... [ 35%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch.py [AutoAPI] Mapping Data... [ 37%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch_base.py [AutoAPI] Mapping Data... [ 39%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata.py [AutoAPI] Mapping Data... [ 41%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata_base.py [AutoAPI] Mapping Data... [ 43%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_raw_request.py [AutoAPI] Mapping Data... [ 46%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_relationships.py [AutoAPI] Mapping Data... [ 48%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_upload.py [AutoAPI] Mapping Data... [ 50%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\__init__.py [AutoAPI] Mapping Data... [ 52%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\extensions\__init__.py [AutoAPI] Mapping Data... [ 54%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\migrate_v0_to_v1.py [AutoAPI] Mapping Data... [ 57%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\__init__.py [AutoAPI] Mapping Data... [ 59%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\batch.py [AutoAPI] Mapping Data... [ 61%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\fetchxml_query.py [AutoAPI] Mapping Data... [ 63%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\filters.py [AutoAPI] Mapping Data... [ 65%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\labels.py [AutoAPI] Mapping Data... [ 67%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\protocol.py [AutoAPI] Mapping Data... [ 70%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\query_builder.py [AutoAPI] Mapping Data... [ 72%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\record.py [AutoAPI] Mapping Data... [ 74%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\relationship.py [AutoAPI] Mapping Data... [ 76%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\table_info.py [AutoAPI] Mapping Data... [ 78%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\upsert.py [AutoAPI] Mapping Data... [ 80%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\__init__.py [AutoAPI] Mapping Data... [ 83%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\batch.py [AutoAPI] Mapping Data... [ 85%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\dataframe.py [AutoAPI] Mapping Data... [ 87%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\files.py [AutoAPI] Mapping Data... [ 89%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\query.py [AutoAPI] Mapping Data... [ 91%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\records.py [AutoAPI] Mapping Data... [ 93%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\tables.py [AutoAPI] Mapping Data... [ 96%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\__init__.py [AutoAPI] Mapping Data... [ 98%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\_pandas.py [AutoAPI] Mapping Data... [100%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\__init__.py -[AutoAPI] Rendering Data... [ 3%] PowerPlatform -[AutoAPI] Rendering Data... [ 6%] PowerPlatform.Dataverse -[AutoAPI] Rendering Data... [ 9%] PowerPlatform.Dataverse.core -[AutoAPI] Rendering Data... [ 12%] PowerPlatform.Dataverse.data -[AutoAPI] Rendering Data... [ 15%] PowerPlatform.Dataverse.utils -[AutoAPI] Rendering Data... [ 18%] PowerPlatform.Dataverse.client -[AutoAPI] Rendering Data... [ 21%] PowerPlatform.Dataverse.common -[AutoAPI] Rendering Data... [ 24%] PowerPlatform.Dataverse.models -[AutoAPI] Rendering Data... [ 27%] PowerPlatform.Dataverse.migration -[AutoAPI] Rendering Data... [ 30%] PowerPlatform.Dataverse.extensions -[AutoAPI] Rendering Data... [ 33%] PowerPlatform.Dataverse.operations -[AutoAPI] Rendering Data... [ 36%] PowerPlatform.Dataverse.core.config -[AutoAPI] Rendering Data... [ 39%] PowerPlatform.Dataverse.core.errors -[AutoAPI] Rendering Data... [ 42%] PowerPlatform.Dataverse.claude_skill -[AutoAPI] Rendering Data... [ 45%] PowerPlatform.Dataverse.models.batch -[AutoAPI] Rendering Data... [ 48%] PowerPlatform.Dataverse.models.labels -[AutoAPI] Rendering Data... [ 52%] PowerPlatform.Dataverse.models.record -[AutoAPI] Rendering Data... [ 55%] PowerPlatform.Dataverse.models.upsert -[AutoAPI] Rendering Data... [ 58%] PowerPlatform.Dataverse.models.filters -[AutoAPI] Rendering Data... [ 61%] PowerPlatform.Dataverse.core.log_config -[AutoAPI] Rendering Data... [ 64%] PowerPlatform.Dataverse.models.protocol -[AutoAPI] Rendering Data... [ 67%] PowerPlatform.Dataverse.common.constants -[AutoAPI] Rendering Data... [ 70%] PowerPlatform.Dataverse.operations.batch -[AutoAPI] Rendering Data... [ 73%] PowerPlatform.Dataverse.operations.files -[AutoAPI] Rendering Data... [ 76%] PowerPlatform.Dataverse.operations.query -[AutoAPI] Rendering Data... [ 79%] PowerPlatform.Dataverse.models.table_info -[AutoAPI] Rendering Data... [ 82%] PowerPlatform.Dataverse.operations.tables -[AutoAPI] Rendering Data... [ 85%] PowerPlatform.Dataverse.operations.records -[AutoAPI] Rendering Data... [ 88%] PowerPlatform.Dataverse.models.relationship -[AutoAPI] Rendering Data... [ 91%] PowerPlatform.Dataverse.models.query_builder -[AutoAPI] Rendering Data... [ 94%] PowerPlatform.Dataverse.operations.dataframe -[AutoAPI] Rendering Data... [ 97%] PowerPlatform.Dataverse.models.fetchxml_query -[AutoAPI] Rendering Data... [100%] PowerPlatform.Dataverse.migration.migrate_v0_to_v1 - -[autosummary] generating autosummary for: index.rst -building [mo]: targets for 0 po files that are out of date -writing output...  -building [html]: targets for 1 source files that are out of date -updating environment: [new config] 35 added, 0 changed, 0 removed -reading sources... [ 3%] autoapi/PowerPlatform/Dataverse/claude_skill/index reading sources... [ 6%] autoapi/PowerPlatform/Dataverse/client/index reading sources... [ 9%] autoapi/PowerPlatform/Dataverse/common/constants/index reading sources... [ 11%] autoapi/PowerPlatform/Dataverse/common/index reading sources... [ 14%] autoapi/PowerPlatform/Dataverse/core/config/index reading sources... [ 17%] autoapi/PowerPlatform/Dataverse/core/errors/index reading sources... [ 20%] autoapi/PowerPlatform/Dataverse/core/index reading sources... [ 23%] autoapi/PowerPlatform/Dataverse/core/log_config/index reading sources... [ 26%] autoapi/PowerPlatform/Dataverse/data/index reading sources... [ 29%] autoapi/PowerPlatform/Dataverse/extensions/index reading sources... [ 31%] autoapi/PowerPlatform/Dataverse/index reading sources... [ 34%] autoapi/PowerPlatform/Dataverse/migration/index reading sources... [ 37%] autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index reading sources... [ 40%] autoapi/PowerPlatform/Dataverse/models/batch/index reading sources... [ 43%] autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index reading sources... [ 46%] autoapi/PowerPlatform/Dataverse/models/filters/index reading sources... [ 49%] autoapi/PowerPlatform/Dataverse/models/index reading sources... [ 51%] autoapi/PowerPlatform/Dataverse/models/labels/index reading sources... [ 54%] autoapi/PowerPlatform/Dataverse/models/protocol/index reading sources... [ 57%] autoapi/PowerPlatform/Dataverse/models/query_builder/index reading sources... [ 60%] autoapi/PowerPlatform/Dataverse/models/record/index reading sources... [ 63%] autoapi/PowerPlatform/Dataverse/models/relationship/index reading sources... [ 66%] autoapi/PowerPlatform/Dataverse/models/table_info/index reading sources... [ 69%] autoapi/PowerPlatform/Dataverse/models/upsert/index reading sources... [ 71%] autoapi/PowerPlatform/Dataverse/operations/batch/index reading sources... [ 74%] autoapi/PowerPlatform/Dataverse/operations/dataframe/index reading sources... [ 77%] autoapi/PowerPlatform/Dataverse/operations/files/index reading sources... [ 80%] autoapi/PowerPlatform/Dataverse/operations/index reading sources... [ 83%] autoapi/PowerPlatform/Dataverse/operations/query/index reading sources... [ 86%] autoapi/PowerPlatform/Dataverse/operations/records/index reading sources... [ 89%] autoapi/PowerPlatform/Dataverse/operations/tables/index reading sources... [ 91%] autoapi/PowerPlatform/Dataverse/utils/index reading sources... [ 94%] autoapi/PowerPlatform/index reading sources... [ 97%] autoapi/index reading sources... [100%] index -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:77: ERROR: Unexpected indentation. [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:72: WARNING: Block quote ends without a blank line; unexpected unindent. [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:78: WARNING: Definition list ends without a blank line; unexpected unindent. [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\filters\index.rst:181: ERROR: Malformed table. -Right border not aligned or missing. - -+-----------------+-----------------------------+-------------------------------------+ -| Pattern form | Example | Compiles to | -+=================+=============================+=====================================+ -| ``val%`` | ``like("Contoso%")`` | ``startswith(column,'Contoso')`` | -+-----------------+-----------------------------+-------------------------------------+ -| ``%val`` | ``like("%Ltd")`` | ``endswith(column,'Ltd')`` | -+-----------------+-----------------------------+-------------------------------------+ -| ``%val%`` | ``like("%Corp%")`` | ``contains(column,'Corp')`` | -+-----------------+-----------------------------+-------------------------------------+ -| No wildcard | ``like("Contoso")`` | ``column eq 'Contoso'`` | -+-----------------+-----------------------------+-------------------------------------+ -| Other | ``like("Con%oso")`` | :class:`ValueError` | -+-----------------+-----------------------------+-------------------------------------+ [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\index.rst:354: ERROR: Malformed table. -Right border not aligned or missing. - -+-----------------+-----------------------------+-------------------------------------+ -| Pattern form | Example | Compiles to | -+=================+=============================+=====================================+ -| ``val%`` | ``like("Contoso%")`` | ``startswith(column,'Contoso')`` | -+-----------------+-----------------------------+-------------------------------------+ -| ``%val`` | ``like("%Ltd")`` | ``endswith(column,'Ltd')`` | -+-----------------+-----------------------------+-------------------------------------+ -| ``%val%`` | ``like("%Corp%")`` | ``contains(column,'Corp')`` | -+-----------------+-----------------------------+-------------------------------------+ -| No wildcard | ``like("Contoso")`` | ``column eq 'Contoso'`` | -+-----------------+-----------------------------+-------------------------------------+ -| Other | ``like("Con%oso")`` | :class:`ValueError` | -+-----------------+-----------------------------+-------------------------------------+ [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:1189: WARNING: Inline emphasis start-string without end-string. [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\query\index.rst:114: WARNING: Inline emphasis start-string without end-string. [docutils] -looking for now-outdated files... none found -pickling environment... done -checking consistency... done -preparing documents... done -copying assets...  -copying static files...  -Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\basic.css -Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\documentation_options.js -Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\language_data.js -Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\alabaster.css -copying static files: done -copying extra files...  -copying extra files: done -copying assets: done -writing output... [ 3%] autoapi/PowerPlatform/Dataverse/claude_skill/index writing output... [ 6%] autoapi/PowerPlatform/Dataverse/client/index writing output... [ 9%] autoapi/PowerPlatform/Dataverse/common/constants/index writing output... [ 11%] autoapi/PowerPlatform/Dataverse/common/index writing output... [ 14%] autoapi/PowerPlatform/Dataverse/core/config/index writing output... [ 17%] autoapi/PowerPlatform/Dataverse/core/errors/index writing output... [ 20%] autoapi/PowerPlatform/Dataverse/core/index writing output... [ 23%] autoapi/PowerPlatform/Dataverse/core/log_config/index writing output... [ 26%] autoapi/PowerPlatform/Dataverse/data/index writing output... [ 29%] autoapi/PowerPlatform/Dataverse/extensions/index writing output... [ 31%] autoapi/PowerPlatform/Dataverse/index writing output... [ 34%] autoapi/PowerPlatform/Dataverse/migration/index writing output... [ 37%] autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index writing output... [ 40%] autoapi/PowerPlatform/Dataverse/models/batch/index writing output... [ 43%] autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index writing output... [ 46%] autoapi/PowerPlatform/Dataverse/models/filters/index writing output... [ 49%] autoapi/PowerPlatform/Dataverse/models/index writing output... [ 51%] autoapi/PowerPlatform/Dataverse/models/labels/index writing output... [ 54%] autoapi/PowerPlatform/Dataverse/models/protocol/index writing output... [ 57%] autoapi/PowerPlatform/Dataverse/models/query_builder/index writing output... [ 60%] autoapi/PowerPlatform/Dataverse/models/record/index writing output... [ 63%] autoapi/PowerPlatform/Dataverse/models/relationship/index writing output... [ 66%] autoapi/PowerPlatform/Dataverse/models/table_info/index writing output... [ 69%] autoapi/PowerPlatform/Dataverse/models/upsert/index writing output... [ 71%] autoapi/PowerPlatform/Dataverse/operations/batch/index writing output... [ 74%] autoapi/PowerPlatform/Dataverse/operations/dataframe/index writing output... [ 77%] autoapi/PowerPlatform/Dataverse/operations/files/index writing output... [ 80%] autoapi/PowerPlatform/Dataverse/operations/index writing output... [ 83%] autoapi/PowerPlatform/Dataverse/operations/query/index writing output... [ 86%] autoapi/PowerPlatform/Dataverse/operations/records/index writing output... [ 89%] autoapi/PowerPlatform/Dataverse/operations/tables/index writing output... [ 91%] autoapi/PowerPlatform/Dataverse/utils/index writing output... [ 94%] autoapi/PowerPlatform/index writing output... [ 97%] autoapi/index writing output... [100%] index -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\client\index.rst:18: WARNING: py:class reference target not found: azure.core.credentials.TokenCredential [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\client\index.rst:16: WARNING: py:class reference target not found: azure.core.credentials.TokenCredential [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:33::1: WARNING: py:class reference target not found: Record [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:42::1: WARNING: py:class reference target not found: ColumnProxy [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:46: WARNING: more than one target found for cross-reference 'ColumnProxy': PowerPlatform.Dataverse.models.filters.ColumnProxy, PowerPlatform.Dataverse.models.ColumnProxy [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:48: WARNING: py:class reference target not found: ColumnProxy [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:61: WARNING: py:class reference target not found: ColumnProxy [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:65: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:73: WARNING: py:class reference target not found: FilterExpression [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:152: WARNING: more than one target found for cross-reference 'Record': PowerPlatform.Dataverse.models.Record, PowerPlatform.Dataverse.models.record.Record [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:154: WARNING: py:class reference target not found: Record [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:162: WARNING: py:class reference target not found: Record [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:165: WARNING: more than one target found for cross-reference 'Record': PowerPlatform.Dataverse.models.Record, PowerPlatform.Dataverse.models.record.Record [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:169: WARNING: more than one target found for cross-reference 'Record': PowerPlatform.Dataverse.models.Record, PowerPlatform.Dataverse.models.record.Record [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:44: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:113: WARNING: py:class reference target not found: pathlib.Path [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\index.rst:671: WARNING: py:obj reference target not found: _QueryBuilderBase [ref.obj] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\index.rst:773: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\index.rst:81: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:193: WARNING: py:obj reference target not found: _QueryBuilderBase [ref.obj] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.QueryResult, PowerPlatform.Dataverse.models.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.QueryResult, PowerPlatform.Dataverse.models.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.QueryResult, PowerPlatform.Dataverse.models.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:295: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\record\index.rst:22: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\relationship\index.rst:25: WARNING: more than one target found for cross-reference 'Label': PowerPlatform.Dataverse.models.Label, PowerPlatform.Dataverse.models.labels.Label [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\relationship\index.rst:25: WARNING: more than one target found for cross-reference 'Label': PowerPlatform.Dataverse.models.Label, PowerPlatform.Dataverse.models.labels.Label [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:30: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:93: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:114: WARNING: py:class reference target not found: _BatchContext [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:275: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:324: WARNING: py:class reference target not found: _BatchContext [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:508: WARNING: py:class reference target not found: _BatchContext [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:539: WARNING: py:class reference target not found: _BatchContext [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:563: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:585: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:617: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:28: WARNING: more than one target found for cross-reference 'ValidationError': PowerPlatform.Dataverse.core.errors.ValidationError, PowerPlatform.Dataverse.core.ValidationError [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:28: WARNING: more than one target found for cross-reference 'MetadataError': PowerPlatform.Dataverse.core.errors.MetadataError, PowerPlatform.Dataverse.core.MetadataError [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:28: WARNING: more than one target found for cross-reference 'HttpError': PowerPlatform.Dataverse.core.errors.HttpError, PowerPlatform.Dataverse.core.HttpError [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:54: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:93: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:154: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:154: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:189: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:248: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:52: WARNING: py:class reference target not found: _BatchContext [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:76: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:98: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:130: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:176: WARNING: py:class reference target not found: _BatchContext [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:207: WARNING: py:class reference target not found: _BatchContext [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:368: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:50: WARNING: more than one target found for cross-reference 'ValidationError': PowerPlatform.Dataverse.core.errors.ValidationError, PowerPlatform.Dataverse.core.ValidationError [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:50: WARNING: more than one target found for cross-reference 'MetadataError': PowerPlatform.Dataverse.core.errors.MetadataError, PowerPlatform.Dataverse.core.MetadataError [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:50: WARNING: more than one target found for cross-reference 'HttpError': PowerPlatform.Dataverse.core.errors.HttpError, PowerPlatform.Dataverse.core.HttpError [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:496: WARNING: py:class reference target not found: _BatchContext [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:680: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:701: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:795: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:834: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:895: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:895: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:930: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:989: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: collections.abc.Iterable [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: more than one target found for cross-reference 'UpsertItem': PowerPlatform.Dataverse.models.UpsertItem, PowerPlatform.Dataverse.models.upsert.UpsertItem [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:: WARNING: py:class reference target not found: collections.abc.Iterable [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:: WARNING: more than one target found for cross-reference 'UpsertItem': PowerPlatform.Dataverse.models.UpsertItem, PowerPlatform.Dataverse.models.upsert.UpsertItem [ref.python] -generating indices... genindex py-modindex done -writing additional pages... search done -dumping search index in English (code: en)... done -dumping object inventory... done -build succeeded, 103 warnings. - -The HTML pages are in docs_local\_build. diff --git a/docs_local/build_post_merge.log b/docs_local/build_post_merge.log deleted file mode 100644 index 158a0555..00000000 --- a/docs_local/build_post_merge.log +++ /dev/null @@ -1,134 +0,0 @@ -Running Sphinx v9.1.0 -loading translations [en]... done -making output directory... done -[AutoAPI] Reading files... [ 2%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\__init__.py [AutoAPI] Reading files... [ 4%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\client.py [AutoAPI] Reading files... [ 7%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\_skill_installer.py [AutoAPI] Reading files... [ 9%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\__init__.py [AutoAPI] Reading files... [ 11%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\claude_skill\__init__.py [AutoAPI] Reading files... [ 13%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\constants.py [AutoAPI] Reading files... [ 15%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\__init__.py [AutoAPI] Reading files... [ 17%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\config.py [AutoAPI] Reading files... [ 20%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\errors.py [AutoAPI] Reading files... [ 22%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\log_config.py [AutoAPI] Reading files... [ 24%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_auth.py [AutoAPI] Reading files... [ 26%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_error_codes.py [AutoAPI] Reading files... [ 28%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http.py [AutoAPI] Reading files... [ 30%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http_logger.py [AutoAPI] Reading files... [ 33%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\__init__.py [AutoAPI] Reading files... [ 35%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch.py [AutoAPI] Reading files... [ 37%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch_base.py [AutoAPI] Reading files... [ 39%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata.py [AutoAPI] Reading files... [ 41%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata_base.py [AutoAPI] Reading files... [ 43%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_raw_request.py [AutoAPI] Reading files... [ 46%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_relationships.py [AutoAPI] Reading files... [ 48%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_upload.py [AutoAPI] Reading files... [ 50%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\__init__.py [AutoAPI] Reading files... [ 52%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\extensions\__init__.py [AutoAPI] Reading files... [ 54%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\migrate_v0_to_v1.py [AutoAPI] Reading files... [ 57%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\__init__.py [AutoAPI] Reading files... [ 59%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\batch.py [AutoAPI] Reading files... [ 61%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\fetchxml_query.py [AutoAPI] Reading files... [ 63%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\filters.py [AutoAPI] Reading files... [ 65%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\labels.py [AutoAPI] Reading files... [ 67%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\protocol.py [AutoAPI] Reading files... [ 70%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\query_builder.py [AutoAPI] Reading files... [ 72%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\record.py [AutoAPI] Reading files... [ 74%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\relationship.py [AutoAPI] Reading files... [ 76%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\table_info.py [AutoAPI] Reading files... [ 78%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\upsert.py [AutoAPI] Reading files... [ 80%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\__init__.py [AutoAPI] Reading files... [ 83%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\batch.py [AutoAPI] Reading files... [ 85%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\dataframe.py [AutoAPI] Reading files... [ 87%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\files.py [AutoAPI] Reading files... [ 89%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\query.py [AutoAPI] Reading files... [ 91%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\records.py [AutoAPI] Reading files... [ 93%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\tables.py [AutoAPI] Reading files... [ 96%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\__init__.py [AutoAPI] Reading files... [ 98%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\_pandas.py [AutoAPI] Reading files... [100%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\__init__.py -[AutoAPI] Mapping Data... [ 2%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\__init__.py [AutoAPI] Mapping Data... [ 4%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\client.py [AutoAPI] Mapping Data... [ 7%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\_skill_installer.py [AutoAPI] Mapping Data... [ 9%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\__init__.py [AutoAPI] Mapping Data... [ 11%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\claude_skill\__init__.py [AutoAPI] Mapping Data... [ 13%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\constants.py [AutoAPI] Mapping Data... [ 15%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\__init__.py [AutoAPI] Mapping Data... [ 17%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\config.py [AutoAPI] Mapping Data... [ 20%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\errors.py [AutoAPI] Mapping Data... [ 22%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\log_config.py [AutoAPI] Mapping Data... [ 24%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_auth.py [AutoAPI] Mapping Data... [ 26%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_error_codes.py [AutoAPI] Mapping Data... [ 28%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http.py [AutoAPI] Mapping Data... [ 30%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http_logger.py [AutoAPI] Mapping Data... [ 33%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\__init__.py [AutoAPI] Mapping Data... [ 35%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch.py [AutoAPI] Mapping Data... [ 37%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch_base.py [AutoAPI] Mapping Data... [ 39%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata.py [AutoAPI] Mapping Data... [ 41%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata_base.py [AutoAPI] Mapping Data... [ 43%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_raw_request.py [AutoAPI] Mapping Data... [ 46%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_relationships.py [AutoAPI] Mapping Data... [ 48%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_upload.py [AutoAPI] Mapping Data... [ 50%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\__init__.py [AutoAPI] Mapping Data... [ 52%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\extensions\__init__.py [AutoAPI] Mapping Data... [ 54%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\migrate_v0_to_v1.py [AutoAPI] Mapping Data... [ 57%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\__init__.py [AutoAPI] Mapping Data... [ 59%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\batch.py [AutoAPI] Mapping Data... [ 61%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\fetchxml_query.py [AutoAPI] Mapping Data... [ 63%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\filters.py [AutoAPI] Mapping Data... [ 65%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\labels.py [AutoAPI] Mapping Data... [ 67%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\protocol.py [AutoAPI] Mapping Data... [ 70%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\query_builder.py [AutoAPI] Mapping Data... [ 72%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\record.py [AutoAPI] Mapping Data... [ 74%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\relationship.py [AutoAPI] Mapping Data... [ 76%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\table_info.py [AutoAPI] Mapping Data... [ 78%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\upsert.py [AutoAPI] Mapping Data... [ 80%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\__init__.py [AutoAPI] Mapping Data... [ 83%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\batch.py [AutoAPI] Mapping Data... [ 85%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\dataframe.py [AutoAPI] Mapping Data... [ 87%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\files.py [AutoAPI] Mapping Data... [ 89%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\query.py [AutoAPI] Mapping Data... [ 91%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\records.py [AutoAPI] Mapping Data... [ 93%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\tables.py [AutoAPI] Mapping Data... [ 96%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\__init__.py [AutoAPI] Mapping Data... [ 98%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\_pandas.py [AutoAPI] Mapping Data... [100%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\__init__.py -[AutoAPI] Rendering Data... [ 3%] PowerPlatform -[AutoAPI] Rendering Data... [ 6%] PowerPlatform.Dataverse -[AutoAPI] Rendering Data... [ 9%] PowerPlatform.Dataverse.core -[AutoAPI] Rendering Data... [ 12%] PowerPlatform.Dataverse.data -[AutoAPI] Rendering Data... [ 15%] PowerPlatform.Dataverse.utils -[AutoAPI] Rendering Data... [ 18%] PowerPlatform.Dataverse.client -[AutoAPI] Rendering Data... [ 21%] PowerPlatform.Dataverse.common -[AutoAPI] Rendering Data... [ 24%] PowerPlatform.Dataverse.models -[AutoAPI] Rendering Data... [ 27%] PowerPlatform.Dataverse.migration -[AutoAPI] Rendering Data... [ 30%] PowerPlatform.Dataverse.extensions -[AutoAPI] Rendering Data... [ 33%] PowerPlatform.Dataverse.operations -[AutoAPI] Rendering Data... [ 36%] PowerPlatform.Dataverse.core.config -[AutoAPI] Rendering Data... [ 39%] PowerPlatform.Dataverse.core.errors -[AutoAPI] Rendering Data... [ 42%] PowerPlatform.Dataverse.claude_skill -[AutoAPI] Rendering Data... [ 45%] PowerPlatform.Dataverse.models.batch -[AutoAPI] Rendering Data... [ 48%] PowerPlatform.Dataverse.models.labels -[AutoAPI] Rendering Data... [ 52%] PowerPlatform.Dataverse.models.record -[AutoAPI] Rendering Data... [ 55%] PowerPlatform.Dataverse.models.upsert -[AutoAPI] Rendering Data... [ 58%] PowerPlatform.Dataverse.models.filters -[AutoAPI] Rendering Data... [ 61%] PowerPlatform.Dataverse.core.log_config -[AutoAPI] Rendering Data... [ 64%] PowerPlatform.Dataverse.models.protocol -[AutoAPI] Rendering Data... [ 67%] PowerPlatform.Dataverse.common.constants -[AutoAPI] Rendering Data... [ 70%] PowerPlatform.Dataverse.operations.batch -[AutoAPI] Rendering Data... [ 73%] PowerPlatform.Dataverse.operations.files -[AutoAPI] Rendering Data... [ 76%] PowerPlatform.Dataverse.operations.query -[AutoAPI] Rendering Data... [ 79%] PowerPlatform.Dataverse.models.table_info -[AutoAPI] Rendering Data... [ 82%] PowerPlatform.Dataverse.operations.tables -[AutoAPI] Rendering Data... [ 85%] PowerPlatform.Dataverse.operations.records -[AutoAPI] Rendering Data... [ 88%] PowerPlatform.Dataverse.models.relationship -[AutoAPI] Rendering Data... [ 91%] PowerPlatform.Dataverse.models.query_builder -[AutoAPI] Rendering Data... [ 94%] PowerPlatform.Dataverse.operations.dataframe -[AutoAPI] Rendering Data... [ 97%] PowerPlatform.Dataverse.models.fetchxml_query -[AutoAPI] Rendering Data... [100%] PowerPlatform.Dataverse.migration.migrate_v0_to_v1 - -[autosummary] generating autosummary for: index.rst -building [mo]: targets for 0 po files that are out of date -writing output...  -building [html]: targets for 1 source files that are out of date -updating environment: [new config] 35 added, 0 changed, 0 removed -reading sources... [ 3%] autoapi/PowerPlatform/Dataverse/claude_skill/index reading sources... [ 6%] autoapi/PowerPlatform/Dataverse/client/index reading sources... [ 9%] autoapi/PowerPlatform/Dataverse/common/constants/index reading sources... [ 11%] autoapi/PowerPlatform/Dataverse/common/index reading sources... [ 14%] autoapi/PowerPlatform/Dataverse/core/config/index reading sources... [ 17%] autoapi/PowerPlatform/Dataverse/core/errors/index reading sources... [ 20%] autoapi/PowerPlatform/Dataverse/core/index reading sources... [ 23%] autoapi/PowerPlatform/Dataverse/core/log_config/index reading sources... [ 26%] autoapi/PowerPlatform/Dataverse/data/index reading sources... [ 29%] autoapi/PowerPlatform/Dataverse/extensions/index reading sources... [ 31%] autoapi/PowerPlatform/Dataverse/index reading sources... [ 34%] autoapi/PowerPlatform/Dataverse/migration/index reading sources... [ 37%] autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index reading sources... [ 40%] autoapi/PowerPlatform/Dataverse/models/batch/index reading sources... [ 43%] autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index reading sources... [ 46%] autoapi/PowerPlatform/Dataverse/models/filters/index reading sources... [ 49%] autoapi/PowerPlatform/Dataverse/models/index reading sources... [ 51%] autoapi/PowerPlatform/Dataverse/models/labels/index reading sources... [ 54%] autoapi/PowerPlatform/Dataverse/models/protocol/index reading sources... [ 57%] autoapi/PowerPlatform/Dataverse/models/query_builder/index reading sources... [ 60%] autoapi/PowerPlatform/Dataverse/models/record/index reading sources... [ 63%] autoapi/PowerPlatform/Dataverse/models/relationship/index reading sources... [ 66%] autoapi/PowerPlatform/Dataverse/models/table_info/index reading sources... [ 69%] autoapi/PowerPlatform/Dataverse/models/upsert/index reading sources... [ 71%] autoapi/PowerPlatform/Dataverse/operations/batch/index reading sources... [ 74%] autoapi/PowerPlatform/Dataverse/operations/dataframe/index reading sources... [ 77%] autoapi/PowerPlatform/Dataverse/operations/files/index reading sources... [ 80%] autoapi/PowerPlatform/Dataverse/operations/index reading sources... [ 83%] autoapi/PowerPlatform/Dataverse/operations/query/index reading sources... [ 86%] autoapi/PowerPlatform/Dataverse/operations/records/index reading sources... [ 89%] autoapi/PowerPlatform/Dataverse/operations/tables/index reading sources... [ 91%] autoapi/PowerPlatform/Dataverse/utils/index reading sources... [ 94%] autoapi/PowerPlatform/index reading sources... [ 97%] autoapi/index reading sources... [100%] index -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:77: ERROR: Unexpected indentation. [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:72: WARNING: Block quote ends without a blank line; unexpected unindent. [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:78: WARNING: Definition list ends without a blank line; unexpected unindent. [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\filters\index.rst:181: ERROR: Malformed table. -Right border not aligned or missing. - -+-----------------+-----------------------------+-------------------------------------+ -| Pattern form | Example | Compiles to | -+=================+=============================+=====================================+ -| ``val%`` | ``like("Contoso%")`` | ``startswith(column,'Contoso')`` | -+-----------------+-----------------------------+-------------------------------------+ -| ``%val`` | ``like("%Ltd")`` | ``endswith(column,'Ltd')`` | -+-----------------+-----------------------------+-------------------------------------+ -| ``%val%`` | ``like("%Corp%")`` | ``contains(column,'Corp')`` | -+-----------------+-----------------------------+-------------------------------------+ -| No wildcard | ``like("Contoso")`` | ``column eq 'Contoso'`` | -+-----------------+-----------------------------+-------------------------------------+ -| Other | ``like("Con%oso")`` | :class:`ValueError` | -+-----------------+-----------------------------+-------------------------------------+ [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\query\index.rst:114: WARNING: Inline emphasis start-string without end-string. [docutils] -looking for now-outdated files... none found -pickling environment... done -checking consistency... done -preparing documents... done -copying assets...  -copying static files...  -Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\basic.css -Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\documentation_options.js -Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\language_data.js -Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\alabaster.css -copying static files: done -copying extra files...  -copying extra files: done -copying assets: done -writing output... [ 3%] autoapi/PowerPlatform/Dataverse/claude_skill/index writing output... [ 6%] autoapi/PowerPlatform/Dataverse/client/index writing output... [ 9%] autoapi/PowerPlatform/Dataverse/common/constants/index writing output... [ 11%] autoapi/PowerPlatform/Dataverse/common/index writing output... [ 14%] autoapi/PowerPlatform/Dataverse/core/config/index writing output... [ 17%] autoapi/PowerPlatform/Dataverse/core/errors/index writing output... [ 20%] autoapi/PowerPlatform/Dataverse/core/index writing output... [ 23%] autoapi/PowerPlatform/Dataverse/core/log_config/index writing output... [ 26%] autoapi/PowerPlatform/Dataverse/data/index writing output... [ 29%] autoapi/PowerPlatform/Dataverse/extensions/index writing output... [ 31%] autoapi/PowerPlatform/Dataverse/index writing output... [ 34%] autoapi/PowerPlatform/Dataverse/migration/index writing output... [ 37%] autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index writing output... [ 40%] autoapi/PowerPlatform/Dataverse/models/batch/index writing output... [ 43%] autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index writing output... [ 46%] autoapi/PowerPlatform/Dataverse/models/filters/index writing output... [ 49%] autoapi/PowerPlatform/Dataverse/models/index writing output... [ 51%] autoapi/PowerPlatform/Dataverse/models/labels/index writing output... [ 54%] autoapi/PowerPlatform/Dataverse/models/protocol/index writing output... [ 57%] autoapi/PowerPlatform/Dataverse/models/query_builder/index writing output... [ 60%] autoapi/PowerPlatform/Dataverse/models/record/index writing output... [ 63%] autoapi/PowerPlatform/Dataverse/models/relationship/index writing output... [ 66%] autoapi/PowerPlatform/Dataverse/models/table_info/index writing output... [ 69%] autoapi/PowerPlatform/Dataverse/models/upsert/index writing output... [ 71%] autoapi/PowerPlatform/Dataverse/operations/batch/index writing output... [ 74%] autoapi/PowerPlatform/Dataverse/operations/dataframe/index writing output... [ 77%] autoapi/PowerPlatform/Dataverse/operations/files/index writing output... [ 80%] autoapi/PowerPlatform/Dataverse/operations/index writing output... [ 83%] autoapi/PowerPlatform/Dataverse/operations/query/index writing output... [ 86%] autoapi/PowerPlatform/Dataverse/operations/records/index writing output... [ 89%] autoapi/PowerPlatform/Dataverse/operations/tables/index writing output... [ 91%] autoapi/PowerPlatform/Dataverse/utils/index writing output... [ 94%] autoapi/PowerPlatform/index writing output... [ 97%] autoapi/index writing output... [100%] index -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\client\index.rst:18: WARNING: py:class reference target not found: azure.core.credentials.TokenCredential [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\client\index.rst:16: WARNING: py:class reference target not found: azure.core.credentials.TokenCredential [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:33::1: WARNING: py:class reference target not found: Record [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:42::1: WARNING: py:class reference target not found: ColumnProxy [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:48: WARNING: py:class reference target not found: ColumnProxy [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:61: WARNING: py:class reference target not found: ColumnProxy [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:73: WARNING: py:class reference target not found: FilterExpression [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:154: WARNING: py:class reference target not found: Record [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:162: WARNING: py:class reference target not found: Record [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\index.rst:44: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:113: WARNING: py:class reference target not found: pathlib.Path [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:63::1: WARNING: py:meth reference target not found: QueryBuilder.build [ref.meth] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:72: WARNING: py:meth reference target not found: QueryBuilder.build [ref.meth] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:193: WARNING: py:obj reference target not found: _QueryBuilderBase [ref.obj] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:198: WARNING: py:meth reference target not found: build [ref.meth] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:229: WARNING: py:meth reference target not found: build [ref.meth] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:295: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\record\index.rst:22: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:30: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:93: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:114: WARNING: py:class reference target not found: _BatchContext [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:324: WARNING: py:class reference target not found: _BatchContext [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:508: WARNING: py:class reference target not found: _BatchContext [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:539: WARNING: py:class reference target not found: _BatchContext [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:563: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:585: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:617: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:54: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:93: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:154: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:154: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:189: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:248: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:: WARNING: py:class reference target not found: collections.abc.Iterable [ref.class] -generating indices... genindex py-modindex done -writing additional pages... search done -dumping search index in English (code: en)... done -dumping object inventory... done -build succeeded, 52 warnings. - -The HTML pages are in docs_local\_build. diff --git a/docs_local/build_post_restore.log b/docs_local/build_post_restore.log deleted file mode 100644 index 807500be..00000000 --- a/docs_local/build_post_restore.log +++ /dev/null @@ -1,187 +0,0 @@ -Running Sphinx v9.1.0 -loading translations [en]... done -making output directory... done -[AutoAPI] Reading files... [ 2%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\__init__.py [AutoAPI] Reading files... [ 4%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\client.py [AutoAPI] Reading files... [ 7%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\_skill_installer.py [AutoAPI] Reading files... [ 9%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\__init__.py [AutoAPI] Reading files... [ 11%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\claude_skill\__init__.py [AutoAPI] Reading files... [ 13%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\constants.py [AutoAPI] Reading files... [ 15%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\__init__.py [AutoAPI] Reading files... [ 17%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\config.py [AutoAPI] Reading files... [ 20%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\errors.py [AutoAPI] Reading files... [ 22%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\log_config.py [AutoAPI] Reading files... [ 24%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_auth.py [AutoAPI] Reading files... [ 26%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_error_codes.py [AutoAPI] Reading files... [ 28%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http.py [AutoAPI] Reading files... [ 30%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http_logger.py [AutoAPI] Reading files... [ 33%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\__init__.py [AutoAPI] Reading files... [ 35%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch.py [AutoAPI] Reading files... [ 37%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch_base.py [AutoAPI] Reading files... [ 39%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata.py [AutoAPI] Reading files... [ 41%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata_base.py [AutoAPI] Reading files... [ 43%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_raw_request.py [AutoAPI] Reading files... [ 46%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_relationships.py [AutoAPI] Reading files... [ 48%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_upload.py [AutoAPI] Reading files... [ 50%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\__init__.py [AutoAPI] Reading files... [ 52%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\extensions\__init__.py [AutoAPI] Reading files... [ 54%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\migrate_v0_to_v1.py [AutoAPI] Reading files... [ 57%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\__init__.py [AutoAPI] Reading files... [ 59%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\batch.py [AutoAPI] Reading files... [ 61%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\fetchxml_query.py [AutoAPI] Reading files... [ 63%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\filters.py [AutoAPI] Reading files... [ 65%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\labels.py [AutoAPI] Reading files... [ 67%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\protocol.py [AutoAPI] Reading files... [ 70%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\query_builder.py [AutoAPI] Reading files... [ 72%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\record.py [AutoAPI] Reading files... [ 74%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\relationship.py [AutoAPI] Reading files... [ 76%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\table_info.py [AutoAPI] Reading files... [ 78%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\upsert.py [AutoAPI] Reading files... [ 80%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\__init__.py [AutoAPI] Reading files... [ 83%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\batch.py [AutoAPI] Reading files... [ 85%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\dataframe.py [AutoAPI] Reading files... [ 87%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\files.py [AutoAPI] Reading files... [ 89%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\query.py [AutoAPI] Reading files... [ 91%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\records.py [AutoAPI] Reading files... [ 93%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\tables.py [AutoAPI] Reading files... [ 96%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\__init__.py [AutoAPI] Reading files... [ 98%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\_pandas.py [AutoAPI] Reading files... [100%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\__init__.py -WARNING: Cannot resolve cyclic import: PowerPlatform.Dataverse.client, PowerPlatform.Dataverse.data._odata, PowerPlatform.Dataverse.data._odata_base, PowerPlatform.Dataverse, PowerPlatform.Dataverse.client [autoapi.python_import_resolution] -[AutoAPI] Mapping Data... [ 2%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\__init__.py [AutoAPI] Mapping Data... [ 4%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\client.py [AutoAPI] Mapping Data... [ 7%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\_skill_installer.py [AutoAPI] Mapping Data... [ 9%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\__init__.py [AutoAPI] Mapping Data... [ 11%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\claude_skill\__init__.py [AutoAPI] Mapping Data... [ 13%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\constants.py [AutoAPI] Mapping Data... [ 15%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\__init__.py [AutoAPI] Mapping Data... [ 17%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\config.py [AutoAPI] Mapping Data... [ 20%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\errors.py [AutoAPI] Mapping Data... [ 22%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\log_config.py [AutoAPI] Mapping Data... [ 24%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_auth.py [AutoAPI] Mapping Data... [ 26%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_error_codes.py [AutoAPI] Mapping Data... [ 28%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http.py [AutoAPI] Mapping Data... [ 30%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http_logger.py [AutoAPI] Mapping Data... [ 33%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\__init__.py [AutoAPI] Mapping Data... [ 35%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch.py [AutoAPI] Mapping Data... [ 37%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch_base.py [AutoAPI] Mapping Data... [ 39%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata.py [AutoAPI] Mapping Data... [ 41%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata_base.py [AutoAPI] Mapping Data... [ 43%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_raw_request.py [AutoAPI] Mapping Data... [ 46%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_relationships.py [AutoAPI] Mapping Data... [ 48%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_upload.py [AutoAPI] Mapping Data... [ 50%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\__init__.py [AutoAPI] Mapping Data... [ 52%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\extensions\__init__.py [AutoAPI] Mapping Data... [ 54%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\migrate_v0_to_v1.py [AutoAPI] Mapping Data... [ 57%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\__init__.py [AutoAPI] Mapping Data... [ 59%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\batch.py [AutoAPI] Mapping Data... [ 61%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\fetchxml_query.py [AutoAPI] Mapping Data... [ 63%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\filters.py [AutoAPI] Mapping Data... [ 65%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\labels.py [AutoAPI] Mapping Data... [ 67%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\protocol.py [AutoAPI] Mapping Data... [ 70%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\query_builder.py [AutoAPI] Mapping Data... [ 72%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\record.py [AutoAPI] Mapping Data... [ 74%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\relationship.py [AutoAPI] Mapping Data... [ 76%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\table_info.py [AutoAPI] Mapping Data... [ 78%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\upsert.py [AutoAPI] Mapping Data... [ 80%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\__init__.py [AutoAPI] Mapping Data... [ 83%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\batch.py [AutoAPI] Mapping Data... [ 85%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\dataframe.py [AutoAPI] Mapping Data... [ 87%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\files.py [AutoAPI] Mapping Data... [ 89%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\query.py [AutoAPI] Mapping Data... [ 91%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\records.py [AutoAPI] Mapping Data... [ 93%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\tables.py [AutoAPI] Mapping Data... [ 96%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\__init__.py [AutoAPI] Mapping Data... [ 98%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\_pandas.py [AutoAPI] Mapping Data... [100%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\__init__.py -[AutoAPI] Rendering Data... [ 3%] PowerPlatform -[AutoAPI] Rendering Data... [ 6%] PowerPlatform.Dataverse -[AutoAPI] Rendering Data... [ 9%] PowerPlatform.Dataverse.core -[AutoAPI] Rendering Data... [ 12%] PowerPlatform.Dataverse.data -[AutoAPI] Rendering Data... [ 15%] PowerPlatform.Dataverse.utils -[AutoAPI] Rendering Data... [ 18%] PowerPlatform.Dataverse.client -[AutoAPI] Rendering Data... [ 21%] PowerPlatform.Dataverse.common -[AutoAPI] Rendering Data... [ 24%] PowerPlatform.Dataverse.models -[AutoAPI] Rendering Data... [ 27%] PowerPlatform.Dataverse.migration -[AutoAPI] Rendering Data... [ 30%] PowerPlatform.Dataverse.extensions -[AutoAPI] Rendering Data... [ 33%] PowerPlatform.Dataverse.operations -[AutoAPI] Rendering Data... [ 36%] PowerPlatform.Dataverse.core.config -[AutoAPI] Rendering Data... [ 39%] PowerPlatform.Dataverse.core.errors -[AutoAPI] Rendering Data... [ 42%] PowerPlatform.Dataverse.claude_skill -[AutoAPI] Rendering Data... [ 45%] PowerPlatform.Dataverse.models.batch -[AutoAPI] Rendering Data... [ 48%] PowerPlatform.Dataverse.models.labels -[AutoAPI] Rendering Data... [ 52%] PowerPlatform.Dataverse.models.record -[AutoAPI] Rendering Data... [ 55%] PowerPlatform.Dataverse.models.upsert -[AutoAPI] Rendering Data... [ 58%] PowerPlatform.Dataverse.models.filters -[AutoAPI] Rendering Data... [ 61%] PowerPlatform.Dataverse.core.log_config -[AutoAPI] Rendering Data... [ 64%] PowerPlatform.Dataverse.models.protocol -[AutoAPI] Rendering Data... [ 67%] PowerPlatform.Dataverse.common.constants -[AutoAPI] Rendering Data... [ 70%] PowerPlatform.Dataverse.operations.batch -[AutoAPI] Rendering Data... [ 73%] PowerPlatform.Dataverse.operations.files -[AutoAPI] Rendering Data... [ 76%] PowerPlatform.Dataverse.operations.query -[AutoAPI] Rendering Data... [ 79%] PowerPlatform.Dataverse.models.table_info -[AutoAPI] Rendering Data... [ 82%] PowerPlatform.Dataverse.operations.tables -[AutoAPI] Rendering Data... [ 85%] PowerPlatform.Dataverse.operations.records -[AutoAPI] Rendering Data... [ 88%] PowerPlatform.Dataverse.models.relationship -[AutoAPI] Rendering Data... [ 91%] PowerPlatform.Dataverse.models.query_builder -[AutoAPI] Rendering Data... [ 94%] PowerPlatform.Dataverse.operations.dataframe -[AutoAPI] Rendering Data... [ 97%] PowerPlatform.Dataverse.models.fetchxml_query -[AutoAPI] Rendering Data... [100%] PowerPlatform.Dataverse.migration.migrate_v0_to_v1 - -[autosummary] generating autosummary for: index.rst -building [mo]: targets for 0 po files that are out of date -writing output...  -building [html]: targets for 1 source files that are out of date -updating environment: [new config] 35 added, 0 changed, 0 removed -reading sources... [ 3%] autoapi/PowerPlatform/Dataverse/claude_skill/index reading sources... [ 6%] autoapi/PowerPlatform/Dataverse/client/index reading sources... [ 9%] autoapi/PowerPlatform/Dataverse/common/constants/index reading sources... [ 11%] autoapi/PowerPlatform/Dataverse/common/index reading sources... [ 14%] autoapi/PowerPlatform/Dataverse/core/config/index reading sources... [ 17%] autoapi/PowerPlatform/Dataverse/core/errors/index reading sources... [ 20%] autoapi/PowerPlatform/Dataverse/core/index reading sources... [ 23%] autoapi/PowerPlatform/Dataverse/core/log_config/index reading sources... [ 26%] autoapi/PowerPlatform/Dataverse/data/index reading sources... [ 29%] autoapi/PowerPlatform/Dataverse/extensions/index reading sources... [ 31%] autoapi/PowerPlatform/Dataverse/index reading sources... [ 34%] autoapi/PowerPlatform/Dataverse/migration/index reading sources... [ 37%] autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index reading sources... [ 40%] autoapi/PowerPlatform/Dataverse/models/batch/index reading sources... [ 43%] autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index reading sources... [ 46%] autoapi/PowerPlatform/Dataverse/models/filters/index reading sources... [ 49%] autoapi/PowerPlatform/Dataverse/models/index reading sources... [ 51%] autoapi/PowerPlatform/Dataverse/models/labels/index reading sources... [ 54%] autoapi/PowerPlatform/Dataverse/models/protocol/index reading sources... [ 57%] autoapi/PowerPlatform/Dataverse/models/query_builder/index reading sources... [ 60%] autoapi/PowerPlatform/Dataverse/models/record/index reading sources... [ 63%] autoapi/PowerPlatform/Dataverse/models/relationship/index reading sources... [ 66%] autoapi/PowerPlatform/Dataverse/models/table_info/index reading sources... [ 69%] autoapi/PowerPlatform/Dataverse/models/upsert/index reading sources... [ 71%] autoapi/PowerPlatform/Dataverse/operations/batch/index reading sources... [ 74%] autoapi/PowerPlatform/Dataverse/operations/dataframe/index reading sources... [ 77%] autoapi/PowerPlatform/Dataverse/operations/files/index reading sources... [ 80%] autoapi/PowerPlatform/Dataverse/operations/index reading sources... [ 83%] autoapi/PowerPlatform/Dataverse/operations/query/index reading sources... [ 86%] autoapi/PowerPlatform/Dataverse/operations/records/index reading sources... [ 89%] autoapi/PowerPlatform/Dataverse/operations/tables/index reading sources... [ 91%] autoapi/PowerPlatform/Dataverse/utils/index reading sources... [ 94%] autoapi/PowerPlatform/index reading sources... [ 97%] autoapi/index reading sources... [100%] index -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:77: ERROR: Unexpected indentation. [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:72: WARNING: Block quote ends without a blank line; unexpected unindent. [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:78: WARNING: Definition list ends without a blank line; unexpected unindent. [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\filters\index.rst:181: ERROR: Malformed table. -Right border not aligned or missing. - -+-----------------+-----------------------------+-------------------------------------+ -| Pattern form | Example | Compiles to | -+=================+=============================+=====================================+ -| ``val%`` | ``like("Contoso%")`` | ``startswith(column,'Contoso')`` | -+-----------------+-----------------------------+-------------------------------------+ -| ``%val`` | ``like("%Ltd")`` | ``endswith(column,'Ltd')`` | -+-----------------+-----------------------------+-------------------------------------+ -| ``%val%`` | ``like("%Corp%")`` | ``contains(column,'Corp')`` | -+-----------------+-----------------------------+-------------------------------------+ -| No wildcard | ``like("Contoso")`` | ``column eq 'Contoso'`` | -+-----------------+-----------------------------+-------------------------------------+ -| Other | ``like("Con%oso")`` | :class:`ValueError` | -+-----------------+-----------------------------+-------------------------------------+ [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\index.rst:354: ERROR: Malformed table. -Right border not aligned or missing. - -+-----------------+-----------------------------+-------------------------------------+ -| Pattern form | Example | Compiles to | -+=================+=============================+=====================================+ -| ``val%`` | ``like("Contoso%")`` | ``startswith(column,'Contoso')`` | -+-----------------+-----------------------------+-------------------------------------+ -| ``%val`` | ``like("%Ltd")`` | ``endswith(column,'Ltd')`` | -+-----------------+-----------------------------+-------------------------------------+ -| ``%val%`` | ``like("%Corp%")`` | ``contains(column,'Corp')`` | -+-----------------+-----------------------------+-------------------------------------+ -| No wildcard | ``like("Contoso")`` | ``column eq 'Contoso'`` | -+-----------------+-----------------------------+-------------------------------------+ -| Other | ``like("Con%oso")`` | :class:`ValueError` | -+-----------------+-----------------------------+-------------------------------------+ [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:1189: WARNING: Inline emphasis start-string without end-string. [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\query\index.rst:114: WARNING: Inline emphasis start-string without end-string. [docutils] -looking for now-outdated files... none found -pickling environment... done -checking consistency... done -preparing documents... done -copying assets...  -copying static files...  -Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\basic.css -Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\documentation_options.js -Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\language_data.js -Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\alabaster.css -copying static files: done -copying extra files...  -copying extra files: done -copying assets: done -writing output... [ 3%] autoapi/PowerPlatform/Dataverse/claude_skill/index writing output... [ 6%] autoapi/PowerPlatform/Dataverse/client/index writing output... [ 9%] autoapi/PowerPlatform/Dataverse/common/constants/index writing output... [ 11%] autoapi/PowerPlatform/Dataverse/common/index writing output... [ 14%] autoapi/PowerPlatform/Dataverse/core/config/index writing output... [ 17%] autoapi/PowerPlatform/Dataverse/core/errors/index writing output... [ 20%] autoapi/PowerPlatform/Dataverse/core/index writing output... [ 23%] autoapi/PowerPlatform/Dataverse/core/log_config/index writing output... [ 26%] autoapi/PowerPlatform/Dataverse/data/index writing output... [ 29%] autoapi/PowerPlatform/Dataverse/extensions/index writing output... [ 31%] autoapi/PowerPlatform/Dataverse/index writing output... [ 34%] autoapi/PowerPlatform/Dataverse/migration/index writing output... [ 37%] autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index writing output... [ 40%] autoapi/PowerPlatform/Dataverse/models/batch/index writing output... [ 43%] autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index writing output... [ 46%] autoapi/PowerPlatform/Dataverse/models/filters/index writing output... [ 49%] autoapi/PowerPlatform/Dataverse/models/index writing output... [ 51%] autoapi/PowerPlatform/Dataverse/models/labels/index writing output... [ 54%] autoapi/PowerPlatform/Dataverse/models/protocol/index writing output... [ 57%] autoapi/PowerPlatform/Dataverse/models/query_builder/index writing output... [ 60%] autoapi/PowerPlatform/Dataverse/models/record/index writing output... [ 63%] autoapi/PowerPlatform/Dataverse/models/relationship/index writing output... [ 66%] autoapi/PowerPlatform/Dataverse/models/table_info/index writing output... [ 69%] autoapi/PowerPlatform/Dataverse/models/upsert/index writing output... [ 71%] autoapi/PowerPlatform/Dataverse/operations/batch/index writing output... [ 74%] autoapi/PowerPlatform/Dataverse/operations/dataframe/index writing output... [ 77%] autoapi/PowerPlatform/Dataverse/operations/files/index writing output... [ 80%] autoapi/PowerPlatform/Dataverse/operations/index writing output... [ 83%] autoapi/PowerPlatform/Dataverse/operations/query/index writing output... [ 86%] autoapi/PowerPlatform/Dataverse/operations/records/index writing output... [ 89%] autoapi/PowerPlatform/Dataverse/operations/tables/index writing output... [ 91%] autoapi/PowerPlatform/Dataverse/utils/index writing output... [ 94%] autoapi/PowerPlatform/index writing output... [ 97%] autoapi/index writing output... [100%] index -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\client\index.rst:18: WARNING: py:class reference target not found: azure.core.credentials.TokenCredential [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\client\index.rst:16: WARNING: py:class reference target not found: azure.core.credentials.TokenCredential [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:113: WARNING: py:class reference target not found: pathlib.Path [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\index.rst:671: WARNING: py:obj reference target not found: _QueryBuilderBase [ref.obj] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\index.rst:773: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\index.rst:81: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:193: WARNING: py:obj reference target not found: _QueryBuilderBase [ref.obj] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.models.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.models.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: more than one target found for cross-reference 'QueryResult': PowerPlatform.Dataverse.models.QueryResult, PowerPlatform.Dataverse.models.record.QueryResult [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:295: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\record\index.rst:22: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\relationship\index.rst:25: WARNING: more than one target found for cross-reference 'Label': PowerPlatform.Dataverse.models.Label, PowerPlatform.Dataverse.models.labels.Label [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\relationship\index.rst:25: WARNING: more than one target found for cross-reference 'Label': PowerPlatform.Dataverse.models.Label, PowerPlatform.Dataverse.models.labels.Label [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:30: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:93: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:114: WARNING: py:class reference target not found: _BatchContext [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:275: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:324: WARNING: py:class reference target not found: _BatchContext [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:508: WARNING: py:class reference target not found: _BatchContext [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:539: WARNING: py:class reference target not found: _BatchContext [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:563: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:585: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:617: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:28: WARNING: more than one target found for cross-reference 'ValidationError': PowerPlatform.Dataverse.core.errors.ValidationError, PowerPlatform.Dataverse.core.ValidationError [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:28: WARNING: more than one target found for cross-reference 'MetadataError': PowerPlatform.Dataverse.core.errors.MetadataError, PowerPlatform.Dataverse.core.MetadataError [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:28: WARNING: more than one target found for cross-reference 'HttpError': PowerPlatform.Dataverse.core.errors.HttpError, PowerPlatform.Dataverse.core.HttpError [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:54: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:93: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:154: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:154: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:189: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:248: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:52: WARNING: py:class reference target not found: _BatchContext [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:76: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:98: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:130: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:176: WARNING: py:class reference target not found: _BatchContext [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:207: WARNING: py:class reference target not found: _BatchContext [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:368: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:50: WARNING: more than one target found for cross-reference 'ValidationError': PowerPlatform.Dataverse.core.errors.ValidationError, PowerPlatform.Dataverse.core.ValidationError [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:50: WARNING: more than one target found for cross-reference 'MetadataError': PowerPlatform.Dataverse.core.errors.MetadataError, PowerPlatform.Dataverse.core.MetadataError [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:50: WARNING: more than one target found for cross-reference 'HttpError': PowerPlatform.Dataverse.core.errors.HttpError, PowerPlatform.Dataverse.core.HttpError [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:496: WARNING: py:class reference target not found: _BatchContext [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:680: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:701: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:795: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:834: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:895: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:895: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:930: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:989: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: py:class reference target not found: collections.abc.Iterable [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\index.rst:: WARNING: more than one target found for cross-reference 'UpsertItem': PowerPlatform.Dataverse.models.UpsertItem, PowerPlatform.Dataverse.models.upsert.UpsertItem [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:: WARNING: py:class reference target not found: collections.abc.Iterable [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:: WARNING: more than one target found for cross-reference 'FilterExpression': PowerPlatform.Dataverse.models.filters.FilterExpression, PowerPlatform.Dataverse.models.FilterExpression [ref.python] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:: WARNING: more than one target found for cross-reference 'UpsertItem': PowerPlatform.Dataverse.models.UpsertItem, PowerPlatform.Dataverse.models.upsert.UpsertItem [ref.python] -generating indices... genindex py-modindex done -writing additional pages... search done -dumping search index in English (code: en)... done -dumping object inventory... done -build succeeded, 90 warnings. - -The HTML pages are in docs_local\_build. diff --git a/docs_local/build_review.log b/docs_local/build_review.log deleted file mode 100644 index e3b19bf7..00000000 --- a/docs_local/build_review.log +++ /dev/null @@ -1,120 +0,0 @@ -Running Sphinx v9.1.0 -loading translations [en]... done -making output directory... done -[AutoAPI] Reading files... [ 2%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\__init__.py [AutoAPI] Reading files... [ 4%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\client.py [AutoAPI] Reading files... [ 7%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\_skill_installer.py [AutoAPI] Reading files... [ 9%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\__init__.py [AutoAPI] Reading files... [ 11%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\claude_skill\__init__.py [AutoAPI] Reading files... [ 13%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\constants.py [AutoAPI] Reading files... [ 15%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\__init__.py [AutoAPI] Reading files... [ 17%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\config.py [AutoAPI] Reading files... [ 20%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\errors.py [AutoAPI] Reading files... [ 22%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\log_config.py [AutoAPI] Reading files... [ 24%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_auth.py [AutoAPI] Reading files... [ 26%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_error_codes.py [AutoAPI] Reading files... [ 28%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http.py [AutoAPI] Reading files... [ 30%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http_logger.py [AutoAPI] Reading files... [ 33%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\__init__.py [AutoAPI] Reading files... [ 35%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch.py [AutoAPI] Reading files... [ 37%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch_base.py [AutoAPI] Reading files... [ 39%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata.py [AutoAPI] Reading files... [ 41%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata_base.py [AutoAPI] Reading files... [ 43%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_raw_request.py [AutoAPI] Reading files... [ 46%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_relationships.py [AutoAPI] Reading files... [ 48%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_upload.py [AutoAPI] Reading files... [ 50%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\__init__.py [AutoAPI] Reading files... [ 52%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\extensions\__init__.py [AutoAPI] Reading files... [ 54%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\migrate_v0_to_v1.py [AutoAPI] Reading files... [ 57%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\__init__.py [AutoAPI] Reading files... [ 59%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\batch.py [AutoAPI] Reading files... [ 61%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\fetchxml_query.py [AutoAPI] Reading files... [ 63%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\filters.py [AutoAPI] Reading files... [ 65%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\labels.py [AutoAPI] Reading files... [ 67%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\protocol.py [AutoAPI] Reading files... [ 70%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\query_builder.py [AutoAPI] Reading files... [ 72%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\record.py [AutoAPI] Reading files... [ 74%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\relationship.py [AutoAPI] Reading files... [ 76%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\table_info.py [AutoAPI] Reading files... [ 78%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\upsert.py [AutoAPI] Reading files... [ 80%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\__init__.py [AutoAPI] Reading files... [ 83%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\batch.py [AutoAPI] Reading files... [ 85%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\dataframe.py [AutoAPI] Reading files... [ 87%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\files.py [AutoAPI] Reading files... [ 89%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\query.py [AutoAPI] Reading files... [ 91%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\records.py [AutoAPI] Reading files... [ 93%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\tables.py [AutoAPI] Reading files... [ 96%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\__init__.py [AutoAPI] Reading files... [ 98%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\_pandas.py [AutoAPI] Reading files... [100%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\__init__.py -WARNING: Cannot resolve cyclic import: PowerPlatform.Dataverse.client, PowerPlatform.Dataverse.data._odata, PowerPlatform.Dataverse.data._odata_base, PowerPlatform.Dataverse, PowerPlatform.Dataverse.client [autoapi.python_import_resolution] -[AutoAPI] Mapping Data... [ 2%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\__init__.py [AutoAPI] Mapping Data... [ 4%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\client.py [AutoAPI] Mapping Data... [ 7%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\_skill_installer.py [AutoAPI] Mapping Data... [ 9%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\__init__.py [AutoAPI] Mapping Data... [ 11%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\claude_skill\__init__.py [AutoAPI] Mapping Data... [ 13%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\constants.py [AutoAPI] Mapping Data... [ 15%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\common\__init__.py [AutoAPI] Mapping Data... [ 17%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\config.py [AutoAPI] Mapping Data... [ 20%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\errors.py [AutoAPI] Mapping Data... [ 22%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\log_config.py [AutoAPI] Mapping Data... [ 24%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_auth.py [AutoAPI] Mapping Data... [ 26%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_error_codes.py [AutoAPI] Mapping Data... [ 28%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http.py [AutoAPI] Mapping Data... [ 30%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\_http_logger.py [AutoAPI] Mapping Data... [ 33%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\core\__init__.py [AutoAPI] Mapping Data... [ 35%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch.py [AutoAPI] Mapping Data... [ 37%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_batch_base.py [AutoAPI] Mapping Data... [ 39%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata.py [AutoAPI] Mapping Data... [ 41%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_odata_base.py [AutoAPI] Mapping Data... [ 43%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_raw_request.py [AutoAPI] Mapping Data... [ 46%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_relationships.py [AutoAPI] Mapping Data... [ 48%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\_upload.py [AutoAPI] Mapping Data... [ 50%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\data\__init__.py [AutoAPI] Mapping Data... [ 52%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\extensions\__init__.py [AutoAPI] Mapping Data... [ 54%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\migrate_v0_to_v1.py [AutoAPI] Mapping Data... [ 57%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\migration\__init__.py [AutoAPI] Mapping Data... [ 59%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\batch.py [AutoAPI] Mapping Data... [ 61%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\fetchxml_query.py [AutoAPI] Mapping Data... [ 63%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\filters.py [AutoAPI] Mapping Data... [ 65%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\labels.py [AutoAPI] Mapping Data... [ 67%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\protocol.py [AutoAPI] Mapping Data... [ 70%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\query_builder.py [AutoAPI] Mapping Data... [ 72%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\record.py [AutoAPI] Mapping Data... [ 74%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\relationship.py [AutoAPI] Mapping Data... [ 76%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\table_info.py [AutoAPI] Mapping Data... [ 78%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\upsert.py [AutoAPI] Mapping Data... [ 80%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\models\__init__.py [AutoAPI] Mapping Data... [ 83%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\batch.py [AutoAPI] Mapping Data... [ 85%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\dataframe.py [AutoAPI] Mapping Data... [ 87%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\files.py [AutoAPI] Mapping Data... [ 89%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\query.py [AutoAPI] Mapping Data... [ 91%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\records.py [AutoAPI] Mapping Data... [ 93%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\tables.py [AutoAPI] Mapping Data... [ 96%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\operations\__init__.py [AutoAPI] Mapping Data... [ 98%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\_pandas.py [AutoAPI] Mapping Data... [100%] C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\src\PowerPlatform\Dataverse\utils\__init__.py -[AutoAPI] Rendering Data... [ 3%] PowerPlatform -[AutoAPI] Rendering Data... [ 6%] PowerPlatform.Dataverse -[AutoAPI] Rendering Data... [ 9%] PowerPlatform.Dataverse.core -[AutoAPI] Rendering Data... [ 12%] PowerPlatform.Dataverse.data -[AutoAPI] Rendering Data... [ 15%] PowerPlatform.Dataverse.utils -[AutoAPI] Rendering Data... [ 18%] PowerPlatform.Dataverse.client -[AutoAPI] Rendering Data... [ 21%] PowerPlatform.Dataverse.common -[AutoAPI] Rendering Data... [ 24%] PowerPlatform.Dataverse.models -[AutoAPI] Rendering Data... [ 27%] PowerPlatform.Dataverse.migration -[AutoAPI] Rendering Data... [ 30%] PowerPlatform.Dataverse.extensions -[AutoAPI] Rendering Data... [ 33%] PowerPlatform.Dataverse.operations -[AutoAPI] Rendering Data... [ 36%] PowerPlatform.Dataverse.core.config -[AutoAPI] Rendering Data... [ 39%] PowerPlatform.Dataverse.core.errors -[AutoAPI] Rendering Data... [ 42%] PowerPlatform.Dataverse.claude_skill -[AutoAPI] Rendering Data... [ 45%] PowerPlatform.Dataverse.models.batch -[AutoAPI] Rendering Data... [ 48%] PowerPlatform.Dataverse.models.labels -[AutoAPI] Rendering Data... [ 52%] PowerPlatform.Dataverse.models.record -[AutoAPI] Rendering Data... [ 55%] PowerPlatform.Dataverse.models.upsert -[AutoAPI] Rendering Data... [ 58%] PowerPlatform.Dataverse.models.filters -[AutoAPI] Rendering Data... [ 61%] PowerPlatform.Dataverse.core.log_config -[AutoAPI] Rendering Data... [ 64%] PowerPlatform.Dataverse.models.protocol -[AutoAPI] Rendering Data... [ 67%] PowerPlatform.Dataverse.common.constants -[AutoAPI] Rendering Data... [ 70%] PowerPlatform.Dataverse.operations.batch -[AutoAPI] Rendering Data... [ 73%] PowerPlatform.Dataverse.operations.files -[AutoAPI] Rendering Data... [ 76%] PowerPlatform.Dataverse.operations.query -[AutoAPI] Rendering Data... [ 79%] PowerPlatform.Dataverse.models.table_info -[AutoAPI] Rendering Data... [ 82%] PowerPlatform.Dataverse.operations.tables -[AutoAPI] Rendering Data... [ 85%] PowerPlatform.Dataverse.operations.records -[AutoAPI] Rendering Data... [ 88%] PowerPlatform.Dataverse.models.relationship -[AutoAPI] Rendering Data... [ 91%] PowerPlatform.Dataverse.models.query_builder -[AutoAPI] Rendering Data... [ 94%] PowerPlatform.Dataverse.operations.dataframe -[AutoAPI] Rendering Data... [ 97%] PowerPlatform.Dataverse.models.fetchxml_query -[AutoAPI] Rendering Data... [100%] PowerPlatform.Dataverse.migration.migrate_v0_to_v1 - -[autosummary] generating autosummary for: index.rst -building [mo]: targets for 0 po files that are out of date -writing output...  -building [html]: targets for 1 source files that are out of date -updating environment: [new config] 35 added, 0 changed, 0 removed -reading sources... [ 3%] autoapi/PowerPlatform/Dataverse/claude_skill/index reading sources... [ 6%] autoapi/PowerPlatform/Dataverse/client/index reading sources... [ 9%] autoapi/PowerPlatform/Dataverse/common/constants/index reading sources... [ 11%] autoapi/PowerPlatform/Dataverse/common/index reading sources... [ 14%] autoapi/PowerPlatform/Dataverse/core/config/index reading sources... [ 17%] autoapi/PowerPlatform/Dataverse/core/errors/index reading sources... [ 20%] autoapi/PowerPlatform/Dataverse/core/index reading sources... [ 23%] autoapi/PowerPlatform/Dataverse/core/log_config/index reading sources... [ 26%] autoapi/PowerPlatform/Dataverse/data/index reading sources... [ 29%] autoapi/PowerPlatform/Dataverse/extensions/index reading sources... [ 31%] autoapi/PowerPlatform/Dataverse/index reading sources... [ 34%] autoapi/PowerPlatform/Dataverse/migration/index reading sources... [ 37%] autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index reading sources... [ 40%] autoapi/PowerPlatform/Dataverse/models/batch/index reading sources... [ 43%] autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index reading sources... [ 46%] autoapi/PowerPlatform/Dataverse/models/filters/index reading sources... [ 49%] autoapi/PowerPlatform/Dataverse/models/index reading sources... [ 51%] autoapi/PowerPlatform/Dataverse/models/labels/index reading sources... [ 54%] autoapi/PowerPlatform/Dataverse/models/protocol/index reading sources... [ 57%] autoapi/PowerPlatform/Dataverse/models/query_builder/index reading sources... [ 60%] autoapi/PowerPlatform/Dataverse/models/record/index reading sources... [ 63%] autoapi/PowerPlatform/Dataverse/models/relationship/index reading sources... [ 66%] autoapi/PowerPlatform/Dataverse/models/table_info/index reading sources... [ 69%] autoapi/PowerPlatform/Dataverse/models/upsert/index reading sources... [ 71%] autoapi/PowerPlatform/Dataverse/operations/batch/index reading sources... [ 74%] autoapi/PowerPlatform/Dataverse/operations/dataframe/index reading sources... [ 77%] autoapi/PowerPlatform/Dataverse/operations/files/index reading sources... [ 80%] autoapi/PowerPlatform/Dataverse/operations/index reading sources... [ 83%] autoapi/PowerPlatform/Dataverse/operations/query/index reading sources... [ 86%] autoapi/PowerPlatform/Dataverse/operations/records/index reading sources... [ 89%] autoapi/PowerPlatform/Dataverse/operations/tables/index reading sources... [ 91%] autoapi/PowerPlatform/Dataverse/utils/index reading sources... [ 94%] autoapi/PowerPlatform/index reading sources... [ 97%] autoapi/index reading sources... [100%] index -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:77: ERROR: Unexpected indentation. [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:72: WARNING: Block quote ends without a blank line; unexpected unindent. [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:78: WARNING: Definition list ends without a blank line; unexpected unindent. [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\filters\index.rst:181: ERROR: Malformed table. -Right border not aligned or missing. - -+-----------------+-----------------------------+-------------------------------------+ -| Pattern form | Example | Compiles to | -+=================+=============================+=====================================+ -| ``val%`` | ``like("Contoso%")`` | ``startswith(column,'Contoso')`` | -+-----------------+-----------------------------+-------------------------------------+ -| ``%val`` | ``like("%Ltd")`` | ``endswith(column,'Ltd')`` | -+-----------------+-----------------------------+-------------------------------------+ -| ``%val%`` | ``like("%Corp%")`` | ``contains(column,'Corp')`` | -+-----------------+-----------------------------+-------------------------------------+ -| No wildcard | ``like("Contoso")`` | ``column eq 'Contoso'`` | -+-----------------+-----------------------------+-------------------------------------+ -| Other | ``like("Con%oso")`` | :class:`ValueError` | -+-----------------+-----------------------------+-------------------------------------+ [docutils] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\query\index.rst:114: WARNING: Inline emphasis start-string without end-string. [docutils] -looking for now-outdated files... none found -pickling environment... done -checking consistency... done -preparing documents... done -copying assets...  -copying static files...  -Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\basic.css -Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\documentation_options.js -Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\language_data.js -Writing evaluated template result to C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\_build\_static\alabaster.css -copying static files: done -copying extra files...  -copying extra files: done -copying assets: done -writing output... [ 3%] autoapi/PowerPlatform/Dataverse/claude_skill/index writing output... [ 6%] autoapi/PowerPlatform/Dataverse/client/index writing output... [ 9%] autoapi/PowerPlatform/Dataverse/common/constants/index writing output... [ 11%] autoapi/PowerPlatform/Dataverse/common/index writing output... [ 14%] autoapi/PowerPlatform/Dataverse/core/config/index writing output... [ 17%] autoapi/PowerPlatform/Dataverse/core/errors/index writing output... [ 20%] autoapi/PowerPlatform/Dataverse/core/index writing output... [ 23%] autoapi/PowerPlatform/Dataverse/core/log_config/index writing output... [ 26%] autoapi/PowerPlatform/Dataverse/data/index writing output... [ 29%] autoapi/PowerPlatform/Dataverse/extensions/index writing output... [ 31%] autoapi/PowerPlatform/Dataverse/index writing output... [ 34%] autoapi/PowerPlatform/Dataverse/migration/index writing output... [ 37%] autoapi/PowerPlatform/Dataverse/migration/migrate_v0_to_v1/index writing output... [ 40%] autoapi/PowerPlatform/Dataverse/models/batch/index writing output... [ 43%] autoapi/PowerPlatform/Dataverse/models/fetchxml_query/index writing output... [ 46%] autoapi/PowerPlatform/Dataverse/models/filters/index writing output... [ 49%] autoapi/PowerPlatform/Dataverse/models/index writing output... [ 51%] autoapi/PowerPlatform/Dataverse/models/labels/index writing output... [ 54%] autoapi/PowerPlatform/Dataverse/models/protocol/index writing output... [ 57%] autoapi/PowerPlatform/Dataverse/models/query_builder/index writing output... [ 60%] autoapi/PowerPlatform/Dataverse/models/record/index writing output... [ 63%] autoapi/PowerPlatform/Dataverse/models/relationship/index writing output... [ 66%] autoapi/PowerPlatform/Dataverse/models/table_info/index writing output... [ 69%] autoapi/PowerPlatform/Dataverse/models/upsert/index writing output... [ 71%] autoapi/PowerPlatform/Dataverse/operations/batch/index writing output... [ 74%] autoapi/PowerPlatform/Dataverse/operations/dataframe/index writing output... [ 77%] autoapi/PowerPlatform/Dataverse/operations/files/index writing output... [ 80%] autoapi/PowerPlatform/Dataverse/operations/index writing output... [ 83%] autoapi/PowerPlatform/Dataverse/operations/query/index writing output... [ 86%] autoapi/PowerPlatform/Dataverse/operations/records/index writing output... [ 89%] autoapi/PowerPlatform/Dataverse/operations/tables/index writing output... [ 91%] autoapi/PowerPlatform/Dataverse/utils/index writing output... [ 94%] autoapi/PowerPlatform/index writing output... [ 97%] autoapi/index writing output... [100%] index -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\client\index.rst:18: WARNING: py:class reference target not found: azure.core.credentials.TokenCredential [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\client\index.rst:16: WARNING: py:class reference target not found: azure.core.credentials.TokenCredential [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\migration\migrate_v0_to_v1\index.rst:113: WARNING: py:class reference target not found: pathlib.Path [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:193: WARNING: py:obj reference target not found: _QueryBuilderBase [ref.obj] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:295: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\query_builder\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\models\record\index.rst:22: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:30: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:93: WARNING: py:class reference target not found: PowerPlatform.Dataverse.data._batch._ChangeSet [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:114: WARNING: py:class reference target not found: _BatchContext [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:324: WARNING: py:class reference target not found: _BatchContext [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:508: WARNING: py:class reference target not found: _BatchContext [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:539: WARNING: py:class reference target not found: _BatchContext [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:563: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:585: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:617: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\batch\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:54: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:93: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:154: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:154: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:189: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.DataFrame [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:248: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\dataframe\index.rst:: WARNING: py:class reference target not found: pandas.Series [ref.class] -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\docs_local\autoapi\PowerPlatform\Dataverse\operations\records\index.rst:: WARNING: py:class reference target not found: collections.abc.Iterable [ref.class] -generating indices... genindex py-modindex done -writing additional pages... search done -dumping search index in English (code: en)... done -dumping object inventory... done -build succeeded, 38 warnings. - -The HTML pages are in docs_local\_build. diff --git a/docs_local/conf.py b/docs_local/conf.py deleted file mode 100644 index f9c861b2..00000000 --- a/docs_local/conf.py +++ /dev/null @@ -1,41 +0,0 @@ -"""Minimal Sphinx config to reproduce the doc-generation behavior locally.""" - -import os -import sys - -# Make the local source importable -sys.path.insert(0, os.path.abspath("../src")) - -project = "PowerPlatform-Dataverse-Client" -author = "Microsoft Corporation" -release = "0.1.0b11" - -extensions = [ - "sphinx.ext.autodoc", - "sphinx.ext.napoleon", - "autoapi.extension", -] - -# autoapi: auto-discover the package from source -autoapi_type = "python" -autoapi_dirs = ["../src/PowerPlatform"] -autoapi_options = [ - "members", - "undoc-members", - "show-inheritance", - "show-module-summary", - # 'imported-members' deliberately omitted: when re-exports are present in - # __all__ (as in our package __init__.py files), 'imported-members' causes - # autoapi to generate duplicate doc pages — one at the canonical module - # path and one at the re-export path. Without it, only the canonical page - # is generated and 'more than one target' warnings disappear. -] -autoapi_keep_files = True -autoapi_python_class_content = "both" - -# Make warnings visible and counted -nitpicky = True -suppress_warnings = [] - -html_theme = "alabaster" -exclude_patterns = ["_build"] diff --git a/docs_local/index.rst b/docs_local/index.rst deleted file mode 100644 index a3445f40..00000000 --- a/docs_local/index.rst +++ /dev/null @@ -1,8 +0,0 @@ -PowerPlatform Dataverse Client SDK -=================================== - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - - autoapi/index diff --git a/examples/advanced/memo_walkthrough.py b/examples/advanced/memo_walkthrough.py deleted file mode 100644 index 1254e60f..00000000 --- a/examples/advanced/memo_walkthrough.py +++ /dev/null @@ -1,426 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT license. - -""" -Walkthrough demonstrating memo/multiline column type. - -This example exercises memo columns comprehensively: -- Create table with memo column -- Create record with multiline text (newlines) -- Read back and verify multiline text preserved -- Update memo with new multiline content -- Empty string memo -- None/null memo -- Long text (near max length) -- Memo with special characters (quotes, unicode, tabs) -- Memo alongside picklist and other field types -- Verify memo is not mistaken for picklist label - -Prerequisites: -- pip install PowerPlatform-Dataverse-Client -- pip install azure-identity -""" - -import sys -import time -from enum import IntEnum -from azure.identity import InteractiveBrowserCredential -from PowerPlatform.Dataverse.client import DataverseClient -from PowerPlatform.Dataverse.core.errors import MetadataError -import requests - - -def log_call(description): - print(f"\n-> {description}") - - -class Priority(IntEnum): - LOW = 1 - MEDIUM = 2 - HIGH = 3 - - -def backoff(op, *, delays=(0, 2, 5, 10, 20, 20)): - last = None - total_delay = 0 - attempts = 0 - for d in delays: - if d: - time.sleep(d) - total_delay += d - attempts += 1 - try: - result = op() - if attempts > 1: - print(f" [INFO] Backoff succeeded after {attempts - 1} retry(s); waited {total_delay}s total.") - return result - except Exception as ex: - last = ex - continue - if last: - if attempts: - print(f" [WARN] Backoff exhausted after {max(attempts - 1, 0)} retry(s); waited {total_delay}s total.") - raise last - - -assertions_passed = [] - - -def check(label, actual, expected): - if actual == expected: - assertions_passed.append(label) - print(f" [OK] {label}") - else: - print(f" [FAIL] {label}") - print(f" Expected: {expected!r}") - print(f" Actual: {actual!r}") - - -def main(): - print("=" * 80) - print("Memo/Multiline Column Walkthrough") - print("=" * 80) - - print("\n" + "=" * 80) - print("1. Setup & Authentication") - print("=" * 80) - - base_url = sys.argv[1] if len(sys.argv) > 1 else input( - "Enter Dataverse org URL (e.g. https://yourorg.crm.dynamics.com): " - ).strip() - if not base_url: - print("No URL entered; exiting.") - sys.exit(1) - base_url = base_url.rstrip("/") - - log_call("InteractiveBrowserCredential()") - credential = InteractiveBrowserCredential() - - log_call(f"DataverseClient(base_url='{base_url}', credential=...)") - with DataverseClient(base_url=base_url, credential=credential) as client: - print(f"[OK] Connected to: {base_url}") - _run_walkthrough(client) - - -def _run_walkthrough(client): - table_name = "new_MemoDemo" - created_ids = [] - - # ============================================================================ - # 2. Table Creation - # ============================================================================ - print("\n" + "=" * 80) - print("2. Table Creation (memo + string + picklist)") - print("=" * 80) - - log_call(f"client.tables.get('{table_name}')") - table_info = backoff(lambda: client.tables.get(table_name)) - if table_info: - print(f"[OK] Table already exists, deleting for clean test...") - backoff(lambda: client.tables.delete(table_name)) - print(f"[OK] Deleted existing table") - time.sleep(5) - - log_call(f"client.tables.create('{table_name}', columns={{...}})") - columns = { - "new_Title": "string", - "new_Description": "memo", - "new_Priority": Priority, - } - table_info = backoff(lambda: client.tables.create(table_name, columns)) - print(f"[OK] Created table: {table_info.get('table_schema_name')}") - print(f" Columns: {', '.join(table_info.get('columns_created', []))}") - - # ============================================================================ - # 3. Basic Multiline Create & Read - # ============================================================================ - print("\n" + "=" * 80) - print("3. Basic Multiline Create & Read") - print("=" * 80) - - multiline_text = ( - "Subject: Quarterly Performance Review - Q1 2026\n" - "\n" - "Team,\n" - "\n" - "Below is a summary of our Q1 performance across key metrics:\n" - "\n" - "Revenue: $2.4M (up 12% from Q4)\n" - "New customers: 147 (target was 120)\n" - "Customer retention: 94.2%\n" - "Support ticket resolution: avg 4.2 hours\n" - "\n" - "Key highlights:\n" - "- Launched the Python SDK for Dataverse, receiving positive feedback\n" - " from early adopters in the data science community.\n" - "- Reduced API latency by 35% through connection pooling and\n" - " metadata caching optimizations.\n" - "- Onboarded 3 enterprise customers with 10K+ seat deployments.\n" - "\n" - "Areas for improvement:\n" - "- Documentation coverage needs to reach 90% before GA.\n" - "- Integration test suite completion is at 78% (target: 95%).\n" - "- Need to finalize the memo/multiline column support.\n" - "\n" - "Next steps: Schedule individual 1:1s to discuss Q2 goals.\n" - "\n" - "Best regards,\n" - "Engineering Lead" - ) - log_call("client.records.create(...) with multiline memo") - id1 = backoff(lambda: client.records.create(table_name, { - "new_Title": "Basic multiline test", - "new_Description": multiline_text, - "new_Priority": Priority.MEDIUM, - })) - created_ids.append(id1) - print(f"[OK] Created record: {id1}") - - record = backoff(lambda: client.records.get(table_name, id1)) - check("Multiline text preserved on create", - record.get("new_description"), multiline_text) - print(f"\n Stored memo ({len(record.get('new_description', ''))} chars):") - print(" " + "-" * 60) - for line in record.get("new_description", "").split("\n"): - print(f" | {line}") - print(" " + "-" * 60) - - # ============================================================================ - # 4. Update Memo with New Content - # ============================================================================ - print("\n" + "=" * 80) - print("4. Update Memo with New Content") - print("=" * 80) - - updated_text = ( - "UPDATED: Quarterly Performance Review - Q1 2026\n" - "\n" - "Revision notes: Added final numbers after audit.\n" - "\n" - "Revenue: $2.45M (revised up from $2.4M after late invoices)\n" - "New customers: 152 (revised up - 5 deals closed on Mar 31)\n" - "Customer retention: 94.2% (unchanged)\n" - "Support ticket resolution: avg 3.8 hours (improved from 4.2)\n" - "\n" - "Additional Q1 accomplishments:\n" - "- Shipped picklist label resolution optimization (92x faster at scale)\n" - "- Completed memo/multiline column support with full test coverage\n" - "- Published SDK to PyPI with 1,200+ downloads in first week\n" - "\n" - "Q2 priorities:\n" - "1. GA release preparation\n" - "2. Performance benchmarking framework\n" - "3. Expanded relationship management APIs\n" - "\n" - "-- Updated by Engineering Lead, April 2026" - ) - log_call("client.records.update(...) with new multiline memo") - backoff(lambda: client.records.update(table_name, id1, { - "new_Description": updated_text, - })) - record = backoff(lambda: client.records.get(table_name, id1)) - check("Multiline text preserved on update", - record.get("new_description"), updated_text) - - # ============================================================================ - # 5. Empty String Memo - # ============================================================================ - print("\n" + "=" * 80) - print("5. Empty String Memo") - print("=" * 80) - - log_call("client.records.create(...) with empty memo") - id2 = backoff(lambda: client.records.create(table_name, { - "new_Title": "Empty memo test", - "new_Description": "", - })) - created_ids.append(id2) - record = backoff(lambda: client.records.get(table_name, id2)) - # Dataverse may return None for empty strings - val = record.get("new_description") - is_empty = val is None or val == "" - if is_empty: - assertions_passed.append("Empty string stored as null/empty") - print(f" [OK] Empty string stored as null/empty (got: {val!r})") - else: - print(f" [FAIL] Expected null or empty, got: {val!r}") - - # ============================================================================ - # 6. None/Null Memo - # ============================================================================ - print("\n" + "=" * 80) - print("6. None/Null Memo") - print("=" * 80) - - log_call("client.records.create(...) with no memo field") - id3 = backoff(lambda: client.records.create(table_name, { - "new_Title": "No memo provided", - })) - created_ids.append(id3) - record = backoff(lambda: client.records.get(table_name, id3)) - check("Omitted memo field returns None", - record.get("new_description"), None) - - # ============================================================================ - # 7. Special Characters - # ============================================================================ - print("\n" + "=" * 80) - print("7. Special Characters") - print("=" * 80) - - special_text = ( - "Quotes: \"double\" and 'single'\n" - "Tabs:\there\tand\there\n" - "Unicode: cafe\u0301 \u2603 \u2764\n" - "Angle brackets: \n" - "Ampersand: A & B" - ) - log_call("client.records.create(...) with special characters") - id4 = backoff(lambda: client.records.create(table_name, { - "new_Title": "Special chars test", - "new_Description": special_text, - })) - created_ids.append(id4) - record = backoff(lambda: client.records.get(table_name, id4)) - check("Special characters preserved", - record.get("new_description"), special_text) - - # ============================================================================ - # 8. Long Text (near max length) - # ============================================================================ - print("\n" + "=" * 80) - print("8. Long Text (near max length)") - print("=" * 80) - - # MemoAttributeMetadata MaxLength is 4000 - long_text = "A" * 3900 + "\n" + "B" * 99 - log_call(f"client.records.create(...) with {len(long_text)} chars") - id5 = backoff(lambda: client.records.create(table_name, { - "new_Title": "Long text test", - "new_Description": long_text, - })) - created_ids.append(id5) - record = backoff(lambda: client.records.get(table_name, id5)) - stored = record.get("new_description") - check("Long text preserved (4000 chars)", - stored, long_text) - - # ============================================================================ - # 9. Memo Alongside Picklist (no interference) - # ============================================================================ - print("\n" + "=" * 80) - print("9. Memo Alongside Picklist (no interference)") - print("=" * 80) - - memo_with_picklist_label = "High" # Same as a picklist label - log_call("client.records.create(...) with memo='High' and picklist='Low'") - id6 = backoff(lambda: client.records.create(table_name, { - "new_Title": "Memo vs picklist test", - "new_Description": memo_with_picklist_label, - "new_Priority": "Low", - })) - created_ids.append(id6) - record = backoff(lambda: client.records.get(table_name, id6)) - check("Memo 'High' not resolved as picklist int", - record.get("new_description"), "High") - check("Picklist 'Low' resolved to int", - record.get("new_priority"), Priority.LOW) - - # ============================================================================ - # 10. Triple-Quoted String (Python multiline syntax) - # ============================================================================ - print("\n" + "=" * 80) - print("10. Triple-Quoted String (Python syntax)") - print("=" * 80) - - triple_text = """Dear Customer, - -Thank you for contacting Contoso Support regarding your recent order #12345. - -We have thoroughly reviewed your case and determined the following status -for each item in your order: - - Item A (Wireless Keyboard) - Shipped via FedEx, tracking #1Z999AA10123456784 - Expected delivery: April 5, 2026 - - Item B (USB-C Hub) - Currently on backorder due to supply chain delays. - Estimated availability: 2-3 weeks. We will ship immediately when - stock arrives and send you an updated tracking number. - - Item C (Screen Protector) - This item was damaged during fulfillment. - A full refund of $24.99 has been processed to your original payment - method. Please allow 5-7 business days for the credit to appear. - -If you would like to substitute Item B with an alternative product or -cancel that portion of your order, please reply to this message or -call us at 1-800-555-0199 (Mon-Fri, 8am-6pm PST). - -We sincerely apologize for any inconvenience and appreciate your -patience and continued business. - -Best regards, -Sarah Johnson -Customer Support Specialist -Contoso Corporation -Ref: CASE-2026-04-001""" - - log_call("client.records.create(...) with triple-quoted multiline") - id7 = backoff(lambda: client.records.create(table_name, { - "new_Title": "Triple-quoted test", - "new_Description": triple_text, - })) - created_ids.append(id7) - record = backoff(lambda: client.records.get(table_name, id7)) - check("Triple-quoted multiline preserved", - record.get("new_description"), triple_text) - - # ============================================================================ - # 11. Update Memo to None (clear field) - # ============================================================================ - print("\n" + "=" * 80) - print("11. Update Memo to None (clear field)") - print("=" * 80) - - log_call("client.records.update(...) set memo to None") - backoff(lambda: client.records.update(table_name, id1, { - "new_Description": None, - })) - record = backoff(lambda: client.records.get(table_name, id1)) - check("Memo cleared to None", - record.get("new_description"), None) - - # ============================================================================ - # 12. Cleanup - # ============================================================================ - print("\n" + "=" * 80) - print("12. Cleanup") - print("=" * 80) - - log_call(f"client.records.delete('{table_name}', [{len(created_ids)} IDs])") - backoff(lambda: client.records.delete(table_name, created_ids)) - print(f"[OK] Deleted {len(created_ids)} records") - - log_call(f"client.tables.delete('{table_name}')") - try: - backoff(lambda: client.tables.delete(table_name)) - print(f"[OK] Deleted table: {table_name}") - except Exception as ex: - if "not found" in str(ex).lower(): - print(f"[OK] Table already removed: {table_name}") - else: - raise - - # ============================================================================ - # Summary - # ============================================================================ - print("\n" + "=" * 80) - print("Memo Walkthrough Complete!") - print("=" * 80) - print(f"\nAll assertions passed ({len(assertions_passed)}):") - for a in assertions_passed: - print(f" [OK] {a}") - print("=" * 80) - - -if __name__ == "__main__": - main() diff --git a/examples/advanced/perf_benchmark_live.py b/examples/advanced/perf_benchmark_live.py deleted file mode 100644 index 03f17f59..00000000 --- a/examples/advanced/perf_benchmark_live.py +++ /dev/null @@ -1,255 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT license. - -""" -Live Dataverse performance benchmark for picklist label resolution. - -Creates a temporary table with a configurable number of picklist columns, -then measures wall-clock time and API call count for label resolution -at various scale points. - -Run from repo root: - $env:PYTHONPATH="src"; .conda/python.exe examples/advanced/perf_benchmark_live.py - -Prerequisites: - - pip install PowerPlatform-Dataverse-Client azure-identity - - DATAVERSE_URL environment variable set - - Interactive browser auth (or configure credentials below) - -WARNING: This script creates and deletes a temporary table on your -Dataverse environment. It cleans up after itself, but if interrupted -mid-run, you may need to manually delete the table (new_perfbench_*). -""" - -import os -import sys -import time -import uuid -from enum import IntEnum - -sys.path.insert(0, "src") - -from azure.identity import InteractiveBrowserCredential -from PowerPlatform.Dataverse.client import DataverseClient -from PowerPlatform.Dataverse.core.errors import MetadataError - - -# --------------------------------------------------------------------------- -# Configuration -# --------------------------------------------------------------------------- - -# Scale points: number of picklist columns to create on the test table. -# Dataverse has a max attribute limit (~400-500 custom columns per table), -# so we stay within that range. -SCALE_POINTS = [1, 10, 100, 250, 400] -OPTIONS_PER_PICKLIST = 4 -EXTRA_STRING_FIELDS = 3 # non-picklist string fields added at each scale -REPEAT_CALLS = 3 # warm cache repeat calls - - -# --------------------------------------------------------------------------- -# Helpers -# --------------------------------------------------------------------------- - - -def make_picklist_enum(index: int, num_options: int) -> type: - """Dynamically create an IntEnum for a picklist column.""" - members = {f"Option_{j}": 100000000 + j for j in range(num_options)} - return IntEnum(f"Picklist{index}", members) - - -def backoff(op, *, delays=(0, 3, 10, 20)): - """Retry an operation with exponential backoff.""" - last_err = None - for delay in delays: - if delay: - time.sleep(delay) - try: - return op() - except Exception as e: - last_err = e - print(f" [WARN] Retry after {delay}s: {e}") - raise last_err - - -def create_test_table(client, table_name: str, num_picklists: int) -> dict: - """Create a test table with picklist + string columns.""" - schema = {} - - # Add picklist columns - for i in range(num_picklists): - col_name = f"new_Picklist{i}" - enum_cls = make_picklist_enum(i, OPTIONS_PER_PICKLIST) - schema[col_name] = enum_cls - - # Add plain string columns - for i in range(EXTRA_STRING_FIELDS): - schema[f"new_TextField{i}"] = "string" - - print(f" [INFO] Creating table {table_name} with {num_picklists} picklists + {EXTRA_STRING_FIELDS} text fields...") - info = client.tables.create(table_name, schema) - print(f" [OK] Table created: {info['entity_set_name']}") - return info - - -def build_test_record(num_picklists: int) -> dict: - """Build a record with label strings for all picklists + plain text.""" - record = {"new_name": f"perf-test-{uuid.uuid4().hex[:8]}"} - for i in range(num_picklists): - # Use the first option label - record[f"new_picklist{i}"] = "Option_0" - for i in range(EXTRA_STRING_FIELDS): - record[f"new_textfield{i}"] = f"plain text value {i}" - return record - - -def count_api_calls(client, table_name: str, record: dict) -> tuple: - """Measure a single _convert_labels_to_ints call. - - Returns (elapsed_ms, api_call_count). - We count calls by patching _request temporarily. - """ - odata = client._odata - original_request = odata._request - call_count = 0 - - def counting_request(*args, **kwargs): - nonlocal call_count - call_count += 1 - return original_request(*args, **kwargs) - - odata._request = counting_request - try: - t0 = time.perf_counter() - odata._convert_labels_to_ints(table_name, record) - elapsed = time.perf_counter() - t0 - finally: - odata._request = original_request - - return round(elapsed * 1000, 1), call_count - - -# --------------------------------------------------------------------------- -# Main benchmark -# --------------------------------------------------------------------------- - - -def main(): - url = sys.argv[1] if len(sys.argv) > 1 else os.environ.get("DATAVERSE_URL") - if not url: - url = input("Enter Dataverse URL (e.g. https://org.crm.dynamics.com): ").strip() - if not url: - print("[ERR] Dataverse URL required (pass as argument, set DATAVERSE_URL, or enter at prompt).") - sys.exit(1) - - print("=" * 78) - print("Picklist Label Resolution - Live Dataverse Benchmark") - print(f"Environment: {url}") - print(f"Scale points (picklist columns): {SCALE_POINTS}") - print(f"Options per picklist: {OPTIONS_PER_PICKLIST}") - print(f"Extra string fields: {EXTRA_STRING_FIELDS}") - print(f"Warm cache repeat calls: {REPEAT_CALLS}") - print("=" * 78) - - cred = InteractiveBrowserCredential() - client = DataverseClient(base_url=url, credential=cred) - - results = [] - run_id = uuid.uuid4().hex[:6] - - for scale_idx, num_picklists in enumerate(SCALE_POINTS): - table_name = f"new_PerfBench{run_id}_{num_picklists}p" - total_fields = num_picklists + EXTRA_STRING_FIELDS - print(f"\n--- Scale point {scale_idx + 1}/{len(SCALE_POINTS)}: " - f"{num_picklists} picklists, {total_fields} total fields ---") - - try: - # Create table - info = backoff(lambda: create_test_table(client, table_name, num_picklists)) - - # Wait for columns to be visible - print(f" [INFO] Waiting for columns to become visible...") - time.sleep(5) - - # Build test record - record = build_test_record(num_picklists) - - # Flush cache to ensure cold start - client._odata._picklist_label_cache.clear() - - # Cold cache measurement - print(f" [INFO] Cold cache measurement...") - cold_ms, cold_calls = count_api_calls(client, table_name, record) - print(f" [OK] Cold: {cold_calls} API calls, {cold_ms}ms") - - # Warm cache measurements - warm_times = [] - warm_calls_list = [] - for rep in range(REPEAT_CALLS): - ms, calls = count_api_calls(client, table_name, record) - warm_times.append(ms) - warm_calls_list.append(calls) - - avg_warm_ms = round(sum(warm_times) / len(warm_times), 1) - avg_warm_calls = round(sum(warm_calls_list) / len(warm_calls_list), 1) - print(f" [OK] Warm (avg {REPEAT_CALLS}x): {avg_warm_calls} API calls, {avg_warm_ms}ms") - - results.append({ - "picklists": num_picklists, - "total_fields": total_fields, - "cold_calls": cold_calls, - "cold_ms": cold_ms, - "warm_calls": avg_warm_calls, - "warm_ms": avg_warm_ms, - }) - - except Exception as e: - print(f" [ERR] Scale point failed: {e}") - results.append({ - "picklists": num_picklists, - "total_fields": total_fields, - "cold_calls": "ERR", - "cold_ms": "ERR", - "warm_calls": "ERR", - "warm_ms": "ERR", - }) - - finally: - # Cleanup: delete table - print(f" [INFO] Cleaning up table {table_name}...") - try: - backoff(lambda: client.tables.delete(table_name)) - print(f" [OK] Table deleted.") - except Exception as e: - print(f" [WARN] Cleanup failed: {e}") - - # Print results - print("\n" + "=" * 78) - print("RESULTS") - print("=" * 78) - header = ( - f"{'Picklists':>9} | {'Total':>5} | " - f"{'Cold Calls':>10} | {'Cold ms':>8} | " - f"{'Warm Calls':>10} | {'Warm ms':>8}" - ) - print(header) - print("-" * len(header)) - for r in results: - print( - f"{r['picklists']:>9} | {r['total_fields']:>5} | " - f"{str(r['cold_calls']):>10} | {str(r['cold_ms']):>8} | " - f"{str(r['warm_calls']):>10} | {str(r['warm_ms']):>8}" - ) - - print("\n" + "-" * 78) - print("Notes:") - print(" - Cold = first call with empty cache (metadata fetched from Dataverse)") - print(f" - Warm = average of {REPEAT_CALLS} repeat calls (cache populated)") - print(" - Times include real network latency to Dataverse") - - print("\n[OK] Live benchmark complete.") - client.close() - - -if __name__ == "__main__": - main() diff --git a/examples/advanced/perf_benchmark_mock.py b/examples/advanced/perf_benchmark_mock.py deleted file mode 100644 index 7eeba543..00000000 --- a/examples/advanced/perf_benchmark_mock.py +++ /dev/null @@ -1,309 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT license. - -""" -Mock-based performance benchmark for picklist label resolution. - -Compares API call count and wall-clock time across different scale points -(number of string fields / picklists in a record). Uses mocked HTTP -responses with configurable simulated latency to isolate the algorithmic -difference between approaches. - -Run from repo root: - $env:PYTHONPATH="src"; .conda/python.exe examples/advanced/perf_benchmark_mock.py - -The script works with whatever picklist resolution approach is currently -checked out (Option B or Option C). Switch branches and re-run to compare. -""" - -import sys -import time -from unittest.mock import MagicMock, patch, PropertyMock - -sys.path.insert(0, "src") - -from PowerPlatform.Dataverse.data._odata import _ODataClient - - -# --------------------------------------------------------------------------- -# Configuration -# --------------------------------------------------------------------------- - -SCALE_POINTS = [1, 10, 100, 500, 1000] # number of picklist columns -EXTRA_STRING_FIELDS = 3 # non-picklist string fields (fixed, same as live) -OPTIONS_PER_PICKLIST = 4 # number of options per picklist attribute (same as live) -SIMULATED_LATENCY_MS = 150 # typical Dataverse metadata response time -REPEAT_CALLS = 3 # number of repeat calls to measure cache effect -CSV_OUTPUT = False # set True to emit CSV instead of table - - -# --------------------------------------------------------------------------- -# Helpers -# --------------------------------------------------------------------------- - - -def _make_option(value: int, label: str) -> dict: - """Build a realistic OptionMetadata payload.""" - return { - "Value": value, - "Label": { - "LocalizedLabels": [ - {"Label": label, "LanguageCode": 1033} - ] - }, - } - - -def _build_bulk_response(num_picklists: int, options_per: int) -> dict: - """Build a PicklistAttributeMetadata bulk response for Option C.""" - items = [] - for i in range(num_picklists): - attr_name = f"new_picklist_{i}" - options = [ - _make_option(j, f"Label_{i}_{j}") - for j in range(options_per) - ] - items.append({ - "LogicalName": attr_name, - "OptionSet": {"Options": options}, - }) - return {"value": items} - - -def _build_type_check_response(attr_names: list, picklist_names: set) -> dict: - """Build a CRM.In type-check response for Option B.""" - items = [] - for name in attr_names: - if name in picklist_names: - items.append({ - "LogicalName": name, - "@odata.type": "#Microsoft.Dynamics.CRM.PicklistAttributeMetadata", - }) - return {"value": items} - - -def _build_single_optionset_response(attr_index: int, options_per: int) -> dict: - """Build a single attribute OptionSet response for Option B.""" - options = [ - _make_option(j, f"Label_{attr_index}_{j}") - for j in range(options_per) - ] - return { - "value": [{ - "LogicalName": f"new_picklist_{attr_index}", - "OptionSet": {"Options": options}, - }] - } - - -def _build_record(num_string_fields: int, num_picklists: int) -> dict: - """Build a test record with string values (labels for picklists, plain for others).""" - record = {} - for i in range(num_picklists): - # Use first label option for each picklist - record[f"new_picklist_{i}"] = f"Label_{i}_0" - for i in range(num_string_fields - num_picklists): - record[f"new_textfield_{i}"] = f"some text value {i}" - return record - - -def _create_client() -> _ODataClient: - """Create an _ODataClient with mocked auth.""" - mock_auth = MagicMock() - mock_token = MagicMock() - mock_token.access_token = "fake-token" - mock_auth._acquire_token.return_value = mock_token - - mock_config = MagicMock() - mock_config.http_retries = 0 - mock_config.http_backoff = 0 - mock_config.http_timeout = 30 - mock_config.language_code = 1033 - - client = _ODataClient( - auth=mock_auth, - base_url="https://mock.crm.dynamics.com", - config=mock_config, - ) - return client - - -# --------------------------------------------------------------------------- -# Benchmark runner -# --------------------------------------------------------------------------- - - -def run_benchmark(num_picklists: int) -> dict: - """Run a single scale point and return metrics.""" - num_plain = EXTRA_STRING_FIELDS - num_fields = num_picklists + num_plain - record = _build_record(num_fields, num_picklists) - latency_s = SIMULATED_LATENCY_MS / 1000.0 - - # Track API calls - api_call_count = 0 - - def counting_request(method, url, **kwargs): - """Mock _request that counts calls and adds simulated latency.""" - nonlocal api_call_count - api_call_count += 1 - time.sleep(latency_s) - - mock_resp = MagicMock() - mock_resp.status_code = 200 - - # Detect which kind of request this is by URL pattern - url_lower = url.lower() - - # Option C: PicklistAttributeMetadata bulk fetch - if "picklistattributemetadata" in url_lower: - mock_resp.json.return_value = _build_bulk_response(num_picklists, OPTIONS_PER_PICKLIST) - return mock_resp - - # Option B: CRM.In batch type check - if "crm.in" in url_lower or "microsoft.dynamics.crm.in" in url_lower: - picklist_names = {f"new_picklist_{i}" for i in range(num_picklists)} - all_names = list(record.keys()) - mock_resp.json.return_value = _build_type_check_response(all_names, picklist_names) - return mock_resp - - # Option B: individual optionset fetch (per-attribute) - if "optionset" in url_lower or "globaloptionsetdefinitions" in url_lower: - # Extract attr index from URL heuristically - for i in range(num_picklists): - if f"new_picklist_{i}" in url_lower: - mock_resp.json.return_value = _build_single_optionset_response(i, OPTIONS_PER_PICKLIST) - return mock_resp - mock_resp.json.return_value = {"value": []} - return mock_resp - - # Option B fallback: per-attribute type check (old approach) - if "attributetype" in url_lower or "attributes" in url_lower: - # Check if URL references a picklist attribute - for i in range(num_picklists): - if f"new_picklist_{i}" in url_lower: - mock_resp.json.return_value = { - "value": [{ - "LogicalName": f"new_picklist_{i}", - "@odata.type": "#Microsoft.Dynamics.CRM.PicklistAttributeMetadata", - "AttributeType": "Picklist", - }] - } - return mock_resp - # Plain string attribute - mock_resp.json.return_value = { - "value": [{ - "@odata.type": "#Microsoft.Dynamics.CRM.StringAttributeMetadata", - "AttributeType": "String", - }] - } - return mock_resp - - # Default fallback - mock_resp.json.return_value = {"value": []} - return mock_resp - - # --- Cold cache run --- - client = _create_client() - with patch.object(client, "_request", side_effect=counting_request): - api_call_count = 0 - t0 = time.perf_counter() - result = client._convert_labels_to_ints("new_perftable", record) - cold_time = time.perf_counter() - t0 - cold_calls = api_call_count - - # Verify resolution worked (at least some labels should be ints now) - resolved_count = sum(1 for v in result.values() if isinstance(v, int)) - - # --- Warm cache runs --- - warm_times = [] - warm_calls_list = [] - for _ in range(REPEAT_CALLS): - with patch.object(client, "_request", side_effect=counting_request): - api_call_count = 0 - t0 = time.perf_counter() - client._convert_labels_to_ints("new_perftable", record) - warm_times.append(time.perf_counter() - t0) - warm_calls_list.append(api_call_count) - - avg_warm_time = sum(warm_times) / len(warm_times) - avg_warm_calls = sum(warm_calls_list) / len(warm_calls_list) - - return { - "fields": num_fields, - "picklists": num_picklists, - "plain_strings": num_plain, - "cold_calls": cold_calls, - "cold_time_ms": round(cold_time * 1000, 1), - "warm_calls": round(avg_warm_calls, 1), - "warm_time_ms": round(avg_warm_time * 1000, 1), - "resolved": resolved_count, - } - - -def main(): - print("=" * 78) - print("Picklist Label Resolution - Mock Performance Benchmark") - print(f"Simulated latency: {SIMULATED_LATENCY_MS}ms per API call") - print(f"Extra string fields: {EXTRA_STRING_FIELDS}") - print(f"Options per picklist: {OPTIONS_PER_PICKLIST}") - print(f"Warm cache repeat calls: {REPEAT_CALLS}") - print("=" * 78) - - results = [] - for n in SCALE_POINTS: - print(f"\n[INFO] Running scale point: {n} picklists ...", end="", flush=True) - r = run_benchmark(n) - results.append(r) - print(f" done (cold={r['cold_calls']} calls, {r['cold_time_ms']}ms)") - - # Print results table - print("\n" + "=" * 78) - print("RESULTS") - print("=" * 78) - header = ( - f"{'Fields':>7} | {'Picklists':>9} | {'Plain':>5} | " - f"{'Cold Calls':>10} | {'Cold ms':>8} | " - f"{'Warm Calls':>10} | {'Warm ms':>8} | {'Resolved':>8}" - ) - print(header) - print("-" * len(header)) - for r in results: - print( - f"{r['fields']:>7} | {r['picklists']:>9} | {r['plain_strings']:>5} | " - f"{r['cold_calls']:>10} | {r['cold_time_ms']:>8} | " - f"{r['warm_calls']:>10} | {r['warm_time_ms']:>8} | {r['resolved']:>8}" - ) - - print("\n" + "-" * 78) - print("Notes:") - print(f" - Cold = first call (cache empty, metadata fetched)") - print(f" - Warm = average of {REPEAT_CALLS} repeat calls (cache populated)") - print(f" - Resolved = number of label strings converted to ints") - print(f" - Times include {SIMULATED_LATENCY_MS}ms simulated latency per API call") - - if CSV_OUTPUT: - print("\n--- CSV ---") - print("fields,picklists,plain,cold_calls,cold_ms,warm_calls,warm_ms,resolved") - for r in results: - print( - f"{r['fields']},{r['picklists']},{r['plain_strings']}," - f"{r['cold_calls']},{r['cold_time_ms']}," - f"{r['warm_calls']},{r['warm_time_ms']},{r['resolved']}" - ) - - # Summary - first = results[0] - last = results[-1] - print(f"\n[INFO] Scaling: {first['fields']} fields -> {first['cold_calls']} API calls; " - f"{last['fields']} fields -> {last['cold_calls']} API calls") - if last["cold_calls"] <= 2: - print("[INFO] Approach: constant API calls (Option C - bulk fetch)") - elif last["cold_calls"] > first["cold_calls"] * 5: - print("[INFO] Approach: API calls scale with field count (Option B or baseline)") - - print("\n[OK] Benchmark complete.") - - -if __name__ == "__main__": - main() diff --git a/examples/advanced/picklist_walkthrough.py b/examples/advanced/picklist_walkthrough.py deleted file mode 100644 index fba8f8fa..00000000 --- a/examples/advanced/picklist_walkthrough.py +++ /dev/null @@ -1,440 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT license. - -""" -Walkthrough demonstrating picklist label-to-integer resolution. - -This example exercises all edge cases of the SDK's automatic picklist -label resolution, including: -- Single picklist field (create and update) -- Multiple picklist fields in one record -- Mixed picklist + non-picklist string fields -- Integer values passed through unchanged -- Unmatched labels left as strings (graceful fallback) -- Warm cache (second call skips metadata lookups) -- Case-insensitive label matching - -Prerequisites: -- pip install PowerPlatform-Dataverse-Client -- pip install azure-identity -""" - -import json -import sys -import time -from enum import IntEnum -from azure.identity import InteractiveBrowserCredential -from PowerPlatform.Dataverse.client import DataverseClient -from PowerPlatform.Dataverse.core.errors import MetadataError -import requests - - -# Simple logging helper -def log_call(description): - print(f"\n-> {description}") - - -# Two picklist enums to test multiple picklists in one table -class Priority(IntEnum): - LOW = 1 - MEDIUM = 2 - HIGH = 3 - - -class Status(IntEnum): - DRAFT = 100000000 - ACTIVE = 100000001 - CLOSED = 100000002 - - -def backoff(op, *, delays=(0, 2, 5, 10, 20, 20)): - last = None - total_delay = 0 - attempts = 0 - for d in delays: - if d: - time.sleep(d) - total_delay += d - attempts += 1 - try: - result = op() - if attempts > 1: - retry_count = attempts - 1 - print(f" [INFO] Backoff succeeded after {retry_count} retry(s); waited {total_delay}s total.") - return result - except Exception as ex: - last = ex - continue - if last: - if attempts: - retry_count = max(attempts - 1, 0) - print(f" [WARN] Backoff exhausted after {retry_count} retry(s); waited {total_delay}s total.") - raise last - - -def main(): - print("=" * 80) - print("Picklist Label Resolution Walkthrough") - print("=" * 80) - - # ============================================================================ - # 1. SETUP & AUTHENTICATION - # ============================================================================ - print("\n" + "=" * 80) - print("1. Setup & Authentication") - print("=" * 80) - - base_url = sys.argv[1] if len(sys.argv) > 1 else "" - if not base_url: - base_url = input("Enter Dataverse org URL (e.g. https://yourorg.crm.dynamics.com): ").strip() - if not base_url: - print("No URL entered; exiting.") - sys.exit(1) - - base_url = base_url.rstrip("/") - - log_call("InteractiveBrowserCredential()") - credential = InteractiveBrowserCredential() - - log_call(f"DataverseClient(base_url='{base_url}', credential=...)") - with DataverseClient(base_url=base_url, credential=credential) as client: - print(f"[OK] Connected to: {base_url}") - _run_walkthrough(client) - - -def _run_walkthrough(client): - table_name = "new_PicklistDemo" - - # ============================================================================ - # 2. TABLE CREATION WITH TWO PICKLISTS - # ============================================================================ - print("\n" + "=" * 80) - print("2. Table Creation (two picklist columns)") - print("=" * 80) - - log_call(f"client.tables.get('{table_name}')") - table_info = backoff(lambda: client.tables.get(table_name)) - - if table_info: - print(f"[OK] Table already exists: {table_name}") - else: - log_call(f"client.tables.create('{table_name}', columns={{...}})") - columns = { - "new_Title": "string", - "new_Description": "string", - "new_Priority": Priority, - "new_Status": Status, - "new_Count": "int", - } - table_info = backoff(lambda: client.tables.create(table_name, columns)) - print(f"[OK] Created table: {table_name}") - print(f" Columns: {', '.join(table_info.get('columns_created', []))}") - - # ============================================================================ - # 3. SINGLE PICKLIST LABEL (CREATE) - # ============================================================================ - print("\n" + "=" * 80) - print("3. Single Picklist Label (Create)") - print("=" * 80) - - log_call(f"client.records.create('{table_name}', {{'new_Priority': 'High'}})") - record1 = { - "new_Title": "Single picklist test", - "new_Priority": "High", - } - id1 = backoff(lambda: client.records.create(table_name, record1)) - retrieved1 = backoff(lambda: client.records.get(table_name, id1)) - print(f"[OK] Created with label 'High'") - print(f" new_priority = {retrieved1.get('new_priority')} (expected: 3)") - assert retrieved1.get("new_priority") == 3, "Expected 3 for 'High'" - - # Print the cached picklist metadata for visibility - odata = client._get_odata() - table_key = table_name.lower() - cache_entry = odata._picklist_label_cache.get(table_key, {}) - print(f"\n [DEBUG] Picklist cache for '{table_key}':") - picklists = cache_entry.get("picklists", {}) - print(f" Cached {len(picklists)} picklist attribute(s):") - for attr, mapping in picklists.items(): - print(f" {attr}: {json.dumps(mapping, indent=6)}") - - # ============================================================================ - # 4. SINGLE PICKLIST LABEL (UPDATE) - # ============================================================================ - print("\n" + "=" * 80) - print("4. Single Picklist Label (Update)") - print("=" * 80) - - log_call(f"client.records.update('{table_name}', id1, {{'new_Priority': 'Low'}})") - backoff(lambda: client.records.update(table_name, id1, {"new_Priority": "Low"})) - updated1 = backoff(lambda: client.records.get(table_name, id1)) - print(f"[OK] Updated with label 'Low'") - print(f" new_priority = {updated1.get('new_priority')} (expected: 1)") - assert updated1.get("new_priority") == 1, "Expected 1 for 'Low'" - - # ============================================================================ - # 5. MULTIPLE PICKLISTS IN ONE RECORD - # ============================================================================ - print("\n" + "=" * 80) - print("5. Multiple Picklists in One Record") - print("=" * 80) - - log_call(f"client.records.create('{table_name}', {{'new_Priority': 'Medium', 'new_Status': 'Active'}})") - record2 = { - "new_Title": "Two picklists test", - "new_Priority": "Medium", - "new_Status": "Active", - } - id2 = backoff(lambda: client.records.create(table_name, record2)) - retrieved2 = backoff(lambda: client.records.get(table_name, id2)) - print(f"[OK] Created with two picklist labels") - print(f" new_priority = {retrieved2.get('new_priority')} (expected: 2)") - print(f" new_status = {retrieved2.get('new_status')} (expected: 100000001)") - assert retrieved2.get("new_priority") == 2, "Expected 2 for 'Medium'" - assert retrieved2.get("new_status") == 100000001, "Expected 100000001 for 'Active'" - - # ============================================================================ - # 6. MIXED PICKLIST + NON-PICKLIST STRINGS - # ============================================================================ - print("\n" + "=" * 80) - print("6. Mixed Picklist + Non-Picklist Strings") - print("=" * 80) - - log_call(f"client.records.create('{table_name}', {{picklist + string fields}})") - record3 = { - "new_Title": "Mixed fields test", - "new_Description": "This is a plain string, not a picklist", - "new_Priority": "Low", - "new_Status": "Draft", - "new_Count": 42, - } - id3 = backoff(lambda: client.records.create(table_name, record3)) - retrieved3 = backoff(lambda: client.records.get(table_name, id3)) - print(f"[OK] Created with mixed fields") - print(f" new_priority = {retrieved3.get('new_priority')} (expected: 1)") - print(f" new_status = {retrieved3.get('new_status')} (expected: 100000000)") - print(f" new_description = '{retrieved3.get('new_description')}' (expected: unchanged string)") - print(f" new_count = {retrieved3.get('new_count')} (expected: 42)") - assert retrieved3.get("new_priority") == 1, "Expected 1 for 'Low'" - assert retrieved3.get("new_status") == 100000000, "Expected 100000000 for 'Draft'" - assert retrieved3.get("new_description") == "This is a plain string, not a picklist", "String should be unchanged" - assert retrieved3.get("new_count") == 42, "Integer should be unchanged" - - # ============================================================================ - # 7. INTEGER VALUES PASSED THROUGH - # ============================================================================ - print("\n" + "=" * 80) - print("7. Integer Values Passed Through") - print("=" * 80) - - log_call(f"client.records.create('{table_name}', {{'new_Priority': 3}})") - record4 = { - "new_Title": "Integer value test", - "new_Priority": 3, - "new_Status": 100000002, - } - id4 = backoff(lambda: client.records.create(table_name, record4)) - retrieved4 = backoff(lambda: client.records.get(table_name, id4)) - print(f"[OK] Created with integer values (no label resolution needed)") - print(f" new_priority = {retrieved4.get('new_priority')} (expected: 3)") - print(f" new_status = {retrieved4.get('new_status')} (expected: 100000002)") - assert retrieved4.get("new_priority") == 3 - assert retrieved4.get("new_status") == 100000002 - - # ============================================================================ - # 8. UNMATCHED LABEL LEFT AS STRING - # ============================================================================ - print("\n" + "=" * 80) - print("8. Unmatched Label (Graceful Fallback)") - print("=" * 80) - - log_call(f"client.records.create('{table_name}', {{'new_Title': 'UnknownLabel'}})") - # new_Title is a string column, not a picklist -- SDK should pass it through - # even though it looks like it could be a label - record5 = { - "new_Title": "UnknownLabel", - "new_Count": 7, - } - id5 = backoff(lambda: client.records.create(table_name, record5)) - retrieved5 = backoff(lambda: client.records.get(table_name, id5)) - print(f"[OK] String value for non-picklist field passed through") - print(f" new_title = '{retrieved5.get('new_title')}' (expected: 'UnknownLabel')") - assert retrieved5.get("new_title") == "UnknownLabel", "Non-picklist string should be unchanged" - - # ============================================================================ - # 9. CASE-INSENSITIVE LABEL MATCHING - # ============================================================================ - print("\n" + "=" * 80) - print("9. Case-Insensitive Label Matching") - print("=" * 80) - - log_call(f"client.records.create('{table_name}', {{'new_Priority': 'HIGH'}})") - record6 = { - "new_Title": "Case test - uppercase", - "new_Priority": "HIGH", - } - id6 = backoff(lambda: client.records.create(table_name, record6)) - retrieved6 = backoff(lambda: client.records.get(table_name, id6)) - print(f"[OK] 'HIGH' (uppercase) resolved correctly") - print(f" new_priority = {retrieved6.get('new_priority')} (expected: 3)") - assert retrieved6.get("new_priority") == 3, "Case-insensitive match failed" - - log_call(f"client.records.create('{table_name}', {{'new_Priority': 'medium'}})") - record7 = { - "new_Title": "Case test - lowercase", - "new_Priority": "medium", - } - id7 = backoff(lambda: client.records.create(table_name, record7)) - retrieved7 = backoff(lambda: client.records.get(table_name, id7)) - print(f"[OK] 'medium' (lowercase) resolved correctly") - print(f" new_priority = {retrieved7.get('new_priority')} (expected: 2)") - assert retrieved7.get("new_priority") == 2, "Case-insensitive match failed" - - # ============================================================================ - # 10. WARM CACHE (SECOND CALL SKIPS METADATA) - # ============================================================================ - print("\n" + "=" * 80) - print("10. Warm Cache (Second Call)") - print("=" * 80) - - log_call(f"client.records.create('{table_name}', {{...}}) -- should use cached metadata") - record8 = { - "new_Title": "Warm cache test", - "new_Priority": "Low", - "new_Status": "Closed", - "new_Description": "Cache should be warm from previous calls", - } - id8 = backoff(lambda: client.records.create(table_name, record8)) - retrieved8 = backoff(lambda: client.records.get(table_name, id8)) - print(f"[OK] Created using warm cache (no extra metadata calls)") - print(f" new_priority = {retrieved8.get('new_priority')} (expected: 1)") - print(f" new_status = {retrieved8.get('new_status')} (expected: 100000002)") - assert retrieved8.get("new_priority") == 1 - assert retrieved8.get("new_status") == 100000002 - - # ============================================================================ - # 11. UPDATE WITH MULTIPLE PICKLIST LABELS - # ============================================================================ - print("\n" + "=" * 80) - print("11. Update with Multiple Picklist Labels") - print("=" * 80) - - log_call(f"client.records.update('{table_name}', id8, {{'new_Priority': 'High', 'new_Status': 'Active'}})") - backoff( - lambda: client.records.update( - table_name, - id8, - {"new_Priority": "High", "new_Status": "Active"}, - ) - ) - updated8 = backoff(lambda: client.records.get(table_name, id8)) - print(f"[OK] Updated with two picklist labels") - print(f" new_priority = {updated8.get('new_priority')} (expected: 3)") - print(f" new_status = {updated8.get('new_status')} (expected: 100000001)") - assert updated8.get("new_priority") == 3 - assert updated8.get("new_status") == 100000001 - - # ============================================================================ - # 12. MIXED INT AND LABEL IN SAME RECORD - # ============================================================================ - print("\n" + "=" * 80) - print("12. Mixed Integer + Label in Same Record") - print("=" * 80) - - log_call(f"client.records.create('{table_name}', {{'new_Priority': 2, 'new_Status': 'Closed'}})") - record9 = { - "new_Title": "Mixed int+label test", - "new_Priority": 2, # already an int - "new_Status": "Closed", # label to resolve - } - id9 = backoff(lambda: client.records.create(table_name, record9)) - retrieved9 = backoff(lambda: client.records.get(table_name, id9)) - print(f"[OK] Created with int for Priority, label for Status") - print(f" new_priority = {retrieved9.get('new_priority')} (expected: 2, passed through)") - print(f" new_status = {retrieved9.get('new_status')} (expected: 100000002, resolved from 'Closed')") - assert retrieved9.get("new_priority") == 2, "Int value should pass through unchanged" - assert retrieved9.get("new_status") == 100000002, "Expected 100000002 for 'Closed'" - - # ============================================================================ - # 13. FULL REALISTIC UPDATE (picklists + strings + non-strings) - # ============================================================================ - print("\n" + "=" * 80) - print("13. Full Realistic Update (All Field Types)") - print("=" * 80) - - log_call(f"client.records.update('{table_name}', id9, {{picklists + strings + int}})") - backoff( - lambda: client.records.update( - table_name, - id9, - { - "new_Priority": "High", - "new_Status": "Active", - "new_Description": "Updated description text", - "new_Count": 99, - }, - ) - ) - updated9 = backoff(lambda: client.records.get(table_name, id9)) - print(f"[OK] Updated with all field types in one call") - print(f" new_priority = {updated9.get('new_priority')} (expected: 3)") - print(f" new_status = {updated9.get('new_status')} (expected: 100000001)") - print(f" new_description = '{updated9.get('new_description')}' (expected: unchanged string)") - print(f" new_count = {updated9.get('new_count')} (expected: 99)") - assert updated9.get("new_priority") == 3 - assert updated9.get("new_status") == 100000001 - assert updated9.get("new_description") == "Updated description text" - assert updated9.get("new_count") == 99 - - # ============================================================================ - # 14. CLEANUP - # ============================================================================ - print("\n" + "=" * 80) - print("14. Cleanup") - print("=" * 80) - - all_ids = [id1, id2, id3, id4, id5, id6, id7, id8, id9] - - log_call(f"client.records.delete('{table_name}', [{len(all_ids)} IDs])") - backoff(lambda: client.records.delete(table_name, all_ids)) - print(f"[OK] Deleted {len(all_ids)} records") - - log_call(f"client.tables.delete('{table_name}')") - try: - backoff(lambda: client.tables.delete(table_name)) - print(f"[OK] Deleted table: {table_name}") - except MetadataError as ex: - if "not found" in str(ex).lower(): - print(f"[OK] Table already removed: {table_name}") - else: - raise - except Exception as ex: - code = getattr(getattr(ex, "response", None), "status_code", None) - if isinstance(ex, requests.exceptions.HTTPError) and code == 404: - print(f"[OK] Table removed: {table_name}") - else: - raise - - # ============================================================================ - # SUMMARY - # ============================================================================ - print("\n" + "=" * 80) - print("Picklist Walkthrough Complete!") - print("=" * 80) - print("\nAll assertions passed:") - print(" [OK] Single picklist label (create)") - print(" [OK] Single picklist label (update)") - print(" [OK] Multiple picklists in one record") - print(" [OK] Mixed picklist + non-picklist strings") - print(" [OK] Integer values passed through unchanged") - print(" [OK] Unmatched label for non-picklist (graceful fallback)") - print(" [OK] Case-insensitive label matching (uppercase + lowercase)") - print(" [OK] Warm cache (second call uses cached metadata)") - print(" [OK] Update with multiple picklist labels") - print(" [OK] Mixed integer + label in same record") - print(" [OK] Full realistic update (picklists + strings + non-strings)") - print("=" * 80) - - -if __name__ == "__main__": - main() diff --git a/examples/advanced/test_querybuilder_live.py b/examples/advanced/test_querybuilder_live.py deleted file mode 100644 index bcb1437a..00000000 --- a/examples/advanced/test_querybuilder_live.py +++ /dev/null @@ -1,955 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT license. - -""" -Self-contained QueryBuilder live integration test. - -Creates a test table with known data, runs every QueryBuilder method and filter -expression against it, then tears everything down. - -Run: - $env:PYTHONPATH = "src"; python examples/advanced/test_querybuilder_live.py -""" - -import sys -import time -import traceback -from enum import IntEnum - -from azure.identity import InteractiveBrowserCredential -from PowerPlatform.Dataverse.client import DataverseClient -from PowerPlatform.Dataverse.models.filters import ( - eq, - ne, - gt, - ge, - lt, - le, - contains, - startswith, - endswith, - between, - is_null, - is_not_null, - filter_in, - not_in, - not_between, - raw, -) -import requests - - -# --------------------------------------------------------------------------- -# Enum for picklist column -# --------------------------------------------------------------------------- - -class Priority(IntEnum): - LOW = 1 - MEDIUM = 2 - HIGH = 3 - - -# --------------------------------------------------------------------------- -# Constants -# --------------------------------------------------------------------------- - -TABLE = "new_QBLiveTest" -PASSED = 0 -FAILED = 0 -SKIPPED = 0 -ERRORS = [] - - -# --------------------------------------------------------------------------- -# Helpers -# --------------------------------------------------------------------------- - -def backoff(op, *, delays=(0, 2, 5, 10, 20, 20)): - """Retry an operation with exponential backoff.""" - last = None - total_delay = 0 - attempts = 0 - for d in delays: - if d: - time.sleep(d) - total_delay += d - attempts += 1 - try: - result = op() - if attempts > 1: - print(f" [INFO] Backoff succeeded after {attempts - 1} retry(s); waited {total_delay}s.") - return result - except Exception as ex: - last = ex - continue - if last: - if attempts: - print(f" [WARN] Backoff exhausted after {max(attempts - 1, 0)} retry(s); waited {total_delay}s.") - raise last - - -def test(name, fn, client): - """Run a single test and report pass/fail.""" - global PASSED, FAILED, SKIPPED - try: - fn(client) - PASSED += 1 - print(f" [PASS] {name}") - except Exception as ex: - msg = str(ex) - if "query node In is not supported" in msg: - SKIPPED += 1 - print(f" [SKIP] {name}: CRM.In not supported by this environment") - else: - FAILED += 1 - ERRORS.append((name, ex)) - print(f" [FAIL] {name}: {ex}") - traceback.print_exc() - - -# --------------------------------------------------------------------------- -# SETUP: create table + seed data -# --------------------------------------------------------------------------- - -def setup(client): - """Create the test table and seed known data. Returns dict of created IDs.""" - print("\n--- SETUP ---") - - # Delete table if it already exists (clean slate) - try: - existing = client.tables.get(TABLE) - if existing: - print(f" Table '{TABLE}' already exists — deleting first") - backoff(lambda: client.tables.delete(TABLE)) - print(f" Deleted old table") - time.sleep(5) - except Exception: - pass # table doesn't exist, that's fine - - columns = { - "new_Title": "string", - "new_Quantity": "int", - "new_Amount": "decimal", - "new_Completed": "bool", - "new_Priority": Priority, - } - backoff(lambda: client.tables.create(TABLE, columns)) - print(f" Created table: {TABLE}") - - # --- Core records (4 rows with distinct characteristics) --- - core = [ - { - "new_Title": "Complete project documentation", - "new_Quantity": 100, - "new_Amount": 1250.50, - "new_Completed": False, - "new_Priority": Priority.MEDIUM, - }, - { - "new_Title": "Review code changes", - "new_Quantity": 10, - "new_Amount": 500.00, - "new_Completed": True, - "new_Priority": Priority.HIGH, - }, - { - "new_Title": "Update test cases", - "new_Quantity": 8, - "new_Amount": 750.25, - "new_Completed": True, - "new_Priority": Priority.LOW, - }, - { - "new_Title": "Deploy to staging", - "new_Quantity": 3, - "new_Amount": 2000.00, - "new_Completed": False, - "new_Priority": Priority.HIGH, - }, - ] - core_ids = backoff(lambda: client.records.create(TABLE, core)) - print(f" Created {len(core_ids)} core records") - - # --- Paging records (20 rows, Qty 1-20) --- - paging = [ - { - "new_Title": f"Paging test item {i}", - "new_Quantity": i, - "new_Amount": i * 10.0, - "new_Completed": False, - "new_Priority": Priority.LOW, - } - for i in range(1, 21) - ] - paging_ids = backoff(lambda: client.records.create(TABLE, paging)) - print(f" Created {len(paging_ids)} paging records") - - return {"core_ids": core_ids, "paging_ids": paging_ids} - - -# --------------------------------------------------------------------------- -# TEARDOWN: delete all records + table -# --------------------------------------------------------------------------- - -def teardown(client, ids_dict): - """Delete created records and the test table.""" - print("\n--- TEARDOWN ---") - - all_ids = ids_dict.get("core_ids", []) + ids_dict.get("paging_ids", []) - - # Bulk delete records - if all_ids: - try: - job_id = backoff(lambda: client.records.delete(TABLE, all_ids)) - print(f" Bulk delete job: {job_id} ({len(all_ids)} records)") - time.sleep(5) # give bulk delete a moment - except Exception as ex: - print(f" [WARN] Bulk delete failed: {ex}") - - # Delete table - try: - backoff(lambda: client.tables.delete(TABLE)) - print(f" Deleted table: {TABLE}") - except Exception as ex: - print(f" [WARN] Could not delete table: {ex}") - - -# --------------------------------------------------------------------------- -# FLUENT FILTER METHOD TESTS -# --------------------------------------------------------------------------- - -def test_filter_eq(client): - """filter_eq: Completed == False.""" - recs = list( - client.query.builder(TABLE) - .select("new_title", "new_completed") - .filter_eq("new_Completed", False) - .top(25) - .execute() - ) - assert len(recs) > 0, "Expected at least 1 incomplete record" - for r in recs: - assert r["new_completed"] is False, f"Expected False, got {r['new_completed']}" - - -def test_filter_ne(client): - """filter_ne: Priority != LOW.""" - recs = list( - client.query.builder(TABLE) - .select("new_title", "new_priority") - .filter_ne("new_Priority", Priority.LOW) - .top(25) - .execute() - ) - assert len(recs) > 0, "Expected at least 1 non-LOW record" - for r in recs: - assert r["new_priority"] != Priority.LOW, "Got LOW priority unexpectedly" - - -def test_filter_gt(client): - """filter_gt: Quantity > 10.""" - recs = list( - client.query.builder(TABLE) - .select("new_title", "new_quantity") - .filter_gt("new_Quantity", 10) - .execute() - ) - assert len(recs) >= 1, f"Expected >= 1 record with Qty > 10, got {len(recs)}" - for r in recs: - assert r["new_quantity"] > 10, f"Expected > 10, got {r['new_quantity']}" - - -def test_filter_ge(client): - """filter_ge: Quantity >= 10.""" - recs = list( - client.query.builder(TABLE) - .select("new_title", "new_quantity") - .filter_ge("new_Quantity", 10) - .execute() - ) - assert len(recs) >= 2, f"Expected >= 2 records with Qty >= 10, got {len(recs)}" - for r in recs: - assert r["new_quantity"] >= 10, f"Expected >= 10, got {r['new_quantity']}" - - -def test_filter_lt(client): - """filter_lt: Quantity < 5.""" - recs = list( - client.query.builder(TABLE) - .select("new_title", "new_quantity") - .filter_lt("new_Quantity", 5) - .execute() - ) - assert len(recs) >= 1, f"Expected >= 1 record with Qty < 5, got {len(recs)}" - for r in recs: - assert r["new_quantity"] < 5, f"Expected < 5, got {r['new_quantity']}" - - -def test_filter_le(client): - """filter_le: Quantity <= 3.""" - recs = list( - client.query.builder(TABLE) - .select("new_title", "new_quantity") - .filter_le("new_Quantity", 3) - .execute() - ) - assert len(recs) >= 1, f"Expected >= 1 record with Qty <= 3, got {len(recs)}" - for r in recs: - assert r["new_quantity"] <= 3, f"Expected <= 3, got {r['new_quantity']}" - - -def test_filter_contains(client): - """filter_contains: title contains 'Paging'.""" - recs = list( - client.query.builder(TABLE) - .select("new_title") - .filter_contains("new_Title", "Paging") - .execute() - ) - assert len(recs) >= 1, "Expected records containing 'Paging'" - for r in recs: - assert "paging" in r["new_title"].lower(), f"Expected 'Paging' in '{r['new_title']}'" - - -def test_filter_startswith(client): - """filter_startswith: title starts with 'Paging'.""" - recs = list( - client.query.builder(TABLE) - .select("new_title") - .filter_startswith("new_Title", "Paging") - .execute() - ) - assert len(recs) >= 1, "Expected records starting with 'Paging'" - for r in recs: - assert r["new_title"].lower().startswith("paging"), f"'{r['new_title']}' doesn't start with 'Paging'" - - -def test_filter_endswith(client): - """filter_endswith: title ends with 'documentation'.""" - recs = list( - client.query.builder(TABLE) - .select("new_title") - .filter_endswith("new_Title", "documentation") - .execute() - ) - assert len(recs) >= 1, "Expected at least 1 record ending with 'documentation'" - - -def test_filter_null(client): - """filter_null: new_Amount is null (expect 0 — all have amounts).""" - recs = list( - client.query.builder(TABLE) - .select("new_title", "new_amount") - .filter_null("new_Amount") - .execute() - ) - assert isinstance(recs, list), "Expected a list result" - - -def test_filter_not_null(client): - """filter_not_null: new_Amount is not null → all records.""" - recs = list( - client.query.builder(TABLE) - .select("new_title", "new_amount") - .filter_not_null("new_Amount") - .execute() - ) - assert len(recs) >= 20, f"Expected >= 20 records with Amount set, got {len(recs)}" - - -def test_filter_between_decimal(client): - """filter_between: Amount between 500 and 1500.""" - recs = list( - client.query.builder(TABLE) - .select("new_title", "new_amount") - .filter_between("new_Amount", 500, 1500) - .execute() - ) - assert len(recs) >= 1, "Expected >= 1 record with Amount in [500, 1500]" - for r in recs: - assert 500 <= r["new_amount"] <= 1500, f"Amount {r['new_amount']} not in [500, 1500]" - - -def test_filter_between_int(client): - """filter_between: Quantity between 5 and 15.""" - recs = list( - client.query.builder(TABLE) - .select("new_title", "new_quantity") - .filter_between("new_Quantity", 5, 15) - .execute() - ) - assert len(recs) >= 1, "Expected >= 1 record with Quantity in [5, 15]" - for r in recs: - assert 5 <= r["new_quantity"] <= 15, f"Quantity {r['new_quantity']} not in [5, 15]" - - -def test_filter_raw(client): - """filter_raw: raw OData filter string.""" - recs = list( - client.query.builder(TABLE) - .select("new_title", "new_quantity") - .filter_raw("new_quantity ge 10 and new_quantity le 20") - .execute() - ) - assert len(recs) >= 1, "Expected records from raw filter" - for r in recs: - assert 10 <= r["new_quantity"] <= 20, f"Quantity {r['new_quantity']} not in [10, 20]" - - -def test_filter_in_ints(client): - """filter_in: Priority in [HIGH, LOW] using Microsoft.Dynamics.CRM.In.""" - recs = list( - client.query.builder(TABLE) - .select("new_title", "new_priority") - .filter_in("new_Priority", [Priority.HIGH, Priority.LOW]) - .execute() - ) - assert len(recs) >= 2, f"Expected >= 2 records with HIGH or LOW, got {len(recs)}" - for r in recs: - assert r["new_priority"] in (Priority.HIGH, Priority.LOW), ( - f"Expected HIGH or LOW, got {r['new_priority']}" - ) - - -def test_filter_in_strings(client): - """filter_in: title in known values using Microsoft.Dynamics.CRM.In.""" - recs = list( - client.query.builder(TABLE) - .select("new_title") - .filter_in("new_Title", ["Review code changes", "Deploy to staging"]) - .execute() - ) - assert len(recs) == 2, f"Expected 2 records, got {len(recs)}" - titles = {r["new_title"] for r in recs} - assert "Review code changes" in titles - assert "Deploy to staging" in titles - - -def test_filter_in_combined(client): - """filter_in combined with filter_eq via AND.""" - recs = list( - client.query.builder(TABLE) - .select("new_title", "new_priority", "new_completed") - .filter_eq("new_Completed", False) - .filter_in("new_Priority", [Priority.HIGH, Priority.MEDIUM]) - .execute() - ) - assert len(recs) >= 1, "Expected at least 1 record" - for r in recs: - assert r["new_completed"] is False - assert r["new_priority"] in (Priority.HIGH, Priority.MEDIUM) - - -def test_filter_not_in_ints(client): - """filter_not_in: Priority not in [LOW] → HIGH and MEDIUM only.""" - recs = list( - client.query.builder(TABLE) - .select("new_title", "new_priority") - .filter_not_in("new_Priority", [Priority.LOW]) - .top(25) - .execute() - ) - assert len(recs) >= 1, "Expected at least 1 non-LOW record" - for r in recs: - assert r["new_priority"] != Priority.LOW, ( - f"Expected not LOW, got {r['new_priority']}" - ) - - -def test_filter_not_between(client): - """filter_not_between: Quantity not in [5, 15].""" - recs = list( - client.query.builder(TABLE) - .select("new_title", "new_quantity") - .filter_not_between("new_Quantity", 5, 15) - .execute() - ) - assert len(recs) >= 1, "Expected records outside [5, 15]" - for r in recs: - assert r["new_quantity"] < 5 or r["new_quantity"] > 15, ( - f"Quantity {r['new_quantity']} is in [5, 15]" - ) - - -def test_where_not_in(client): - """where() with not_in expression.""" - recs = list( - client.query.builder(TABLE) - .select("new_title", "new_priority") - .where(not_in("new_Priority", [Priority.LOW])) - .top(25) - .execute() - ) - assert len(recs) >= 1, "Expected non-LOW records" - for r in recs: - assert r["new_priority"] != Priority.LOW - - -def test_where_not_between(client): - """where() with not_between expression.""" - recs = list( - client.query.builder(TABLE) - .select("new_title", "new_quantity") - .where(not_between("new_Quantity", 5, 15)) - .execute() - ) - assert len(recs) >= 1, "Expected records outside [5, 15]" - for r in recs: - assert r["new_quantity"] < 5 or r["new_quantity"] > 15 - - -# --------------------------------------------------------------------------- -# WHERE() WITH COMPOSABLE EXPRESSION TREES -# --------------------------------------------------------------------------- - -def test_where_eq(client): - """where() with eq expression.""" - recs = list( - client.query.builder(TABLE) - .select("new_title", "new_completed") - .where(eq("new_Completed", True)) - .execute() - ) - assert len(recs) >= 1, "Expected completed records" - for r in recs: - assert r["new_completed"] is True - - -def test_where_and(client): - """where() with & (AND) operator.""" - recs = list( - client.query.builder(TABLE) - .select("new_title", "new_quantity", "new_completed") - .where(eq("new_Completed", False) & gt("new_Quantity", 5)) - .execute() - ) - assert len(recs) >= 1, "Expected incomplete records with Qty > 5" - for r in recs: - assert r["new_completed"] is False and r["new_quantity"] > 5 - - -def test_where_or(client): - """where() with | (OR) operator.""" - recs = list( - client.query.builder(TABLE) - .select("new_title", "new_quantity") - .where(lt("new_Quantity", 3) | gt("new_Quantity", 18)) - .execute() - ) - assert len(recs) >= 1, "Expected records with Qty < 3 or Qty > 18" - for r in recs: - assert r["new_quantity"] < 3 or r["new_quantity"] > 18 - - -def test_where_not(client): - """where() with ~ (NOT) operator.""" - recs = list( - client.query.builder(TABLE) - .select("new_title", "new_completed") - .where(~eq("new_Completed", True)) - .execute() - ) - assert len(recs) >= 1, "Expected non-completed records" - for r in recs: - assert r["new_completed"] is False - - -def test_where_nested_and_or(client): - """where() with nested (A | B) & C.""" - recs = list( - client.query.builder(TABLE) - .select("new_title", "new_priority", "new_quantity") - .where( - (eq("new_Priority", Priority.HIGH) | eq("new_Priority", Priority.MEDIUM)) - & gt("new_Quantity", 2) - ) - .execute() - ) - assert len(recs) >= 1, "Expected HIGH/MEDIUM priority records with Qty > 2" - for r in recs: - assert r["new_priority"] in (Priority.HIGH, Priority.MEDIUM) - assert r["new_quantity"] > 2 - - -def test_where_contains(client): - """where() with contains expression.""" - recs = list( - client.query.builder(TABLE) - .select("new_title") - .where(contains("new_Title", "test")) - .execute() - ) - assert len(recs) >= 1, "Expected records containing 'test'" - for r in recs: - assert "test" in r["new_title"].lower() - - -def test_where_startswith(client): - """where() with startswith expression.""" - recs = list( - client.query.builder(TABLE) - .select("new_title") - .where(startswith("new_Title", "Deploy")) - .execute() - ) - assert len(recs) >= 1, "Expected records starting with 'Deploy'" - - -def test_where_endswith(client): - """where() with endswith expression.""" - recs = list( - client.query.builder(TABLE) - .select("new_title") - .where(endswith("new_Title", "staging")) - .execute() - ) - assert len(recs) >= 1, "Expected records ending with 'staging'" - - -def test_where_between(client): - """where() with between expression.""" - recs = list( - client.query.builder(TABLE) - .select("new_title", "new_amount") - .where(between("new_Amount", 100, 800)) - .execute() - ) - assert len(recs) >= 1, "Expected records with Amount in [100, 800]" - for r in recs: - assert 100 <= r["new_amount"] <= 800 - - -def test_where_is_null(client): - """where() with is_null (expect 0 — all have amounts).""" - recs = list( - client.query.builder(TABLE) - .select("new_title") - .where(is_null("new_Amount")) - .execute() - ) - assert isinstance(recs, list) - - -def test_where_is_not_null(client): - """where() with is_not_null.""" - recs = list( - client.query.builder(TABLE) - .select("new_title") - .where(is_not_null("new_Amount")) - .execute() - ) - assert len(recs) >= 20 - - -def test_where_raw(client): - """where() with raw expression.""" - recs = list( - client.query.builder(TABLE) - .select("new_title", "new_quantity") - .where(raw("new_quantity eq 100")) - .execute() - ) - assert len(recs) >= 1, "Expected record with Qty=100" - assert recs[0]["new_quantity"] == 100 - - -def test_where_filter_in(client): - """where() with filter_in expression composed with &.""" - recs = list( - client.query.builder(TABLE) - .select("new_title", "new_priority", "new_quantity") - .where(filter_in("new_Priority", [Priority.HIGH, Priority.MEDIUM]) & gt("new_Quantity", 5)) - .execute() - ) - assert len(recs) >= 1, "Expected HIGH/MEDIUM records with Qty > 5" - for r in recs: - assert r["new_priority"] in (Priority.HIGH, Priority.MEDIUM) - assert r["new_quantity"] > 5 - - -# --------------------------------------------------------------------------- -# COMBINED FLUENT + WHERE -# --------------------------------------------------------------------------- - -def test_combined_fluent_and_where(client): - """filter_eq + where(between).""" - recs = list( - client.query.builder(TABLE) - .select("new_title", "new_quantity", "new_completed") - .filter_eq("new_Completed", False) - .where(between("new_Quantity", 5, 15)) - .execute() - ) - assert len(recs) >= 1 - for r in recs: - assert r["new_completed"] is False - assert 5 <= r["new_quantity"] <= 15 - - -def test_combined_multiple_fluent(client): - """Chaining filter_ge + filter_le.""" - recs = list( - client.query.builder(TABLE) - .select("new_title", "new_quantity") - .filter_ge("new_Quantity", 5) - .filter_le("new_Quantity", 15) - .execute() - ) - assert len(recs) >= 1 - for r in recs: - assert 5 <= r["new_quantity"] <= 15 - - -# --------------------------------------------------------------------------- -# SELECT, ORDER_BY, TOP, PAGE_SIZE -# --------------------------------------------------------------------------- - -def test_select_columns(client): - """select() limits returned columns.""" - recs = list( - client.query.builder(TABLE) - .select("new_title") - .top(3) - .execute() - ) - assert len(recs) > 0 - assert "new_title" in recs[0] - - -def test_order_by_asc(client): - """order_by ascending.""" - recs = list( - client.query.builder(TABLE) - .select("new_title", "new_quantity") - .order_by("new_Quantity") - .top(5) - .execute() - ) - assert len(recs) == 5 - quantities = [r["new_quantity"] for r in recs] - assert quantities == sorted(quantities), f"Not ascending: {quantities}" - - -def test_order_by_desc(client): - """order_by descending.""" - recs = list( - client.query.builder(TABLE) - .select("new_title", "new_quantity") - .order_by("new_Quantity", descending=True) - .top(5) - .execute() - ) - assert len(recs) == 5 - quantities = [r["new_quantity"] for r in recs] - assert quantities == sorted(quantities, reverse=True), f"Not descending: {quantities}" - - -def test_top(client): - """top() limits result count.""" - recs = list( - client.query.builder(TABLE) - .select("new_title") - .top(3) - .execute() - ) - assert len(recs) == 3, f"Expected 3 records, got {len(recs)}" - - -def test_page_size_flat(client): - """page_size with flat iteration.""" - recs = list( - client.query.builder(TABLE) - .select("new_title") - .page_size(5) - .execute() - ) - assert len(recs) >= 20, f"Expected >= 20 records, got {len(recs)}" - - -def test_page_size_by_page(client): - """page_size with by_page=True.""" - pages = list( - client.query.builder(TABLE) - .select("new_title") - .page_size(5) - .execute(by_page=True) - ) - assert len(pages) >= 4, f"Expected >= 4 pages (24 records / 5), got {len(pages)}" - for i, p in enumerate(pages[:-1]): - assert len(p) == 5, f"Page {i + 1} has {len(p)} records, expected 5" - - -def test_page_size_by_page_with_filter(client): - """by_page=True with filter.""" - pages = list( - client.query.builder(TABLE) - .select("new_title", "new_quantity") - .filter_between("new_Quantity", 1, 10) - .page_size(3) - .execute(by_page=True) - ) - total = sum(len(p) for p in pages) - assert total >= 1, "Expected at least 1 record in range" - for page in pages: - for r in page: - assert 1 <= r["new_quantity"] <= 10 - - -# --------------------------------------------------------------------------- -# EDGE CASES -# --------------------------------------------------------------------------- - -def test_no_results(client): - """Query returning 0 results → empty list, not error.""" - recs = list( - client.query.builder(TABLE) - .select("new_title") - .filter_eq("new_Quantity", 999999) - .execute() - ) - assert len(recs) == 0, f"Expected 0 results, got {len(recs)}" - - -def test_no_filters(client): - """Query with no filters → all records.""" - recs = list( - client.query.builder(TABLE) - .select("new_title") - .execute() - ) - assert len(recs) >= 24, f"Expected >= 24 records (4 core + 20 paging), got {len(recs)}" - - -def test_chaining_order(client): - """select before/after filter should produce same results.""" - recs1 = list( - client.query.builder(TABLE) - .select("new_title", "new_quantity") - .filter_eq("new_Completed", False) - .order_by("new_Quantity") - .top(5) - .execute() - ) - recs2 = list( - client.query.builder(TABLE) - .filter_eq("new_Completed", False) - .order_by("new_Quantity") - .select("new_title", "new_quantity") - .top(5) - .execute() - ) - titles1 = [r["new_title"] for r in recs1] - titles2 = [r["new_title"] for r in recs2] - assert titles1 == titles2, f"Order mismatch: {titles1} vs {titles2}" - - -# --------------------------------------------------------------------------- -# TEST REGISTRY -# --------------------------------------------------------------------------- - -ALL_TESTS = [ - # Fluent filter methods - ("filter_eq", test_filter_eq), - ("filter_ne", test_filter_ne), - ("filter_gt", test_filter_gt), - ("filter_ge", test_filter_ge), - ("filter_lt", test_filter_lt), - ("filter_le", test_filter_le), - ("filter_contains", test_filter_contains), - ("filter_startswith", test_filter_startswith), - ("filter_endswith", test_filter_endswith), - ("filter_null", test_filter_null), - ("filter_not_null", test_filter_not_null), - ("filter_between (decimal)", test_filter_between_decimal), - ("filter_between (int)", test_filter_between_int), - ("filter_raw", test_filter_raw), - ("filter_in (ints)", test_filter_in_ints), - ("filter_in (strings)", test_filter_in_strings), - ("filter_in + filter_eq combined", test_filter_in_combined), - ("filter_not_in (ints)", test_filter_not_in_ints), - ("filter_not_between", test_filter_not_between), - # where() expression trees - ("where(eq)", test_where_eq), - ("where(& AND)", test_where_and), - ("where(| OR)", test_where_or), - ("where(~ NOT)", test_where_not), - ("where(nested (A|B) & C)", test_where_nested_and_or), - ("where(contains)", test_where_contains), - ("where(startswith)", test_where_startswith), - ("where(endswith)", test_where_endswith), - ("where(between)", test_where_between), - ("where(is_null)", test_where_is_null), - ("where(is_not_null)", test_where_is_not_null), - ("where(raw)", test_where_raw), - ("where(filter_in & gt)", test_where_filter_in), - ("where(not_in)", test_where_not_in), - ("where(not_between)", test_where_not_between), - # Combined - ("combined fluent + where", test_combined_fluent_and_where), - ("combined multiple fluent", test_combined_multiple_fluent), - # Select, ordering, paging - ("select columns", test_select_columns), - ("order_by asc", test_order_by_asc), - ("order_by desc", test_order_by_desc), - ("top()", test_top), - ("page_size flat", test_page_size_flat), - ("page_size by_page", test_page_size_by_page), - ("page_size by_page + filter", test_page_size_by_page_with_filter), - # Edge cases - ("no results", test_no_results), - ("no filters", test_no_filters), - ("chaining order", test_chaining_order), -] - - -# --------------------------------------------------------------------------- -# MAIN -# --------------------------------------------------------------------------- - -def main(): - print("=" * 80) - print("QueryBuilder Comprehensive Live Tests (self-contained)") - print("=" * 80) - - base_url = ( - sys.argv[1] - if len(sys.argv) > 1 - else input("Enter Dataverse org URL (e.g. https://yourorg.crm.dynamics.com): ").strip() - ) - if not base_url: - print("No URL provided; exiting.") - sys.exit(1) - - base_url = base_url.rstrip("/") - credential = InteractiveBrowserCredential() - - ids_dict = {} - with DataverseClient(base_url=base_url, credential=credential) as client: - print(f"Connected to: {base_url}") - - try: - # Setup: create table + seed data - ids_dict = setup(client) - - # Run all tests - print(f"\nRunning {len(ALL_TESTS)} tests against '{TABLE}'...\n") - for name, fn in ALL_TESTS: - test(name, fn, client) - finally: - # Always teardown, even if tests fail - teardown(client, ids_dict) - - print("\n" + "=" * 80) - print(f"Results: {PASSED} passed, {FAILED} failed, {SKIPPED} skipped out of {PASSED + FAILED + SKIPPED}") - print("=" * 80) - - if ERRORS: - print("\nFailed tests:") - for name, ex in ERRORS: - print(f" - {name}: {ex}") - - sys.exit(1 if FAILED else 0) - - -if __name__ == "__main__": - main() diff --git a/forge.txt b/forge.txt deleted file mode 100644 index 64198584..00000000 --- a/forge.txt +++ /dev/null @@ -1,557 +0,0 @@ -Python Server-Side Extension SDK/Package - -We propose a Python server-side extension package that enables customers, agents, and first-party teams to author and run Dataverse server-side logic entirely in Python. This plays a role similar to the XRM SDK used today for developing and deploying Dataverse plug-ins in C#. - -SDK Capabilities - -The Python server-side extension package provides the following capabilities: - -Bring the Dataverse Entity model into Python as strongly typed classes - -Support code-generated, schema-aligned entity types with typed fields, relationships, and option sets - -Provide a SQL-like fluent query and execution API for server-side logic - -Generate Python entity Types directly from a live Dataverse environment - -Comparison to C# plugin Model - -In today's C# plugin model, customers use the IOrganizationService provided through the plugin execution context to query Dataverse using APIs such as QueryExpression, FetchXml, and QueryByAttributes. - -In the Python model, the function execution context exposes an equivalent server-side extension that allows customers or agents to query Dataverse through a more SQL-like fluent API. Under the hood, this extension translates those operations into SDK messages and executes them on the platform using IOrganizationService. - -SDK Message using SQL-Like Fluent Query API - -In the Python model, the function execution context exposes a fluent query interface that allows server-side logic to read and write Dataverse data using a SQL-style API built directly on top of the generated entity types. There is no separate query language to learn — conditions are expressed as Python operator comparisons on typed fields, making invalid field names and type mismatches visible at authoring time. - -Under the hood the fluent API compiles queries to SQL and executes them via IOrganizationService on the Dataverse platform. The caller never writes raw SQL strings. - -Factory functions - -Function - -Returns - -Purpose - -select(*fields) - -SelectQuery[T] - -Start a SELECT statement. Chain .from_() and .where(). - -update(entity) - -UpdateQuery - -Start an UPDATE statement. Chain .set() and .where(). - -insert_into(entity) - -InsertQuery - -Start an INSERT statement. Chain .value(). - - - -Condition operators - -Conditions are formed by applying Python comparison operators directly to typed field descriptors. Multiple conditions are composed of & (AND) and | (OR). Multiple .where() calls are joined with AND at the top level. - -Python expression - -SQL equivalent - -Lead.statecode == 0 - -statecode = ? - -Lead.budgetamount >= 50_000 - -budgetamount >= ? - -Lead.companyname != "Acme" - -companyname != ? - -cond_a & cond_b - -(expr_a AND expr_b) - -cond_a | cond_b - -(expr_a OR expr_b) - -.where(a).where(b) - -WHERE a AND b - - - -Query results - -Every executed query returns a SqlResult[T] instance. The result is generically typed if a SELECT is bound to an entity class the rows are hydrated as instances of that class, not plain dicts. - -property / method - -Type - -Available on - -.records - -list[T] - -SELECT - -.first() - -T | None - -SELECT - -.id - -str | None - -INSERT (GUID of new record) - -.affected - -int - -UPDATE / DELETE - -len(result) - -int - -SELECT - -bool(result) - -bool - -SELECT (True if any rows) - - - -Composing complex conditions - -The & and | operators let you build arbitrarily nested predicates. Parentheses control grouping exactly as in SQL. - -from Types.lead import Lead - - - -# Simple AND — two .where() calls - -select(Lead.leadid) - - .from_(Lead) - - .where(Lead.statecode == 0) - - .where(Lead.budgetamount >= 10_000) - -# → WHERE statecode = ? AND budgetamount >= ? - - - -# OR within a single .where() - -select(Lead.leadid) - - .from_(Lead) - - .where((Lead.budgetamount >= 50_000) | (Lead.industrycode == 7)) - -# → WHERE (budgetamount >= ? OR industrycode = ?) - - - -# Mixed — OR group AND a separate mandatory filter - -select(Lead.leadid, Lead.companyname) - - .from_(Lead) - - .where((Lead.budgetamount >= 50_000) | (Lead.numberofemployees >= 100)) - - .where(Lead.statecode == 0) - -# → WHERE (budgetamount >= ? OR numberofemployees >= ?) AND statecode = ? - - - -End-to-end example — Lead qualification function - -The following example implements the AI agent scenario: evaluate an incoming Lead, update its status, and create the corresponding Account and Contact records. - -from functions import CoreDataverseFunction, FunctionContext - -from functions import select, update, insert_into - -from Types.lead import Lead - -from Types.account import Account - -from Types.contact import Contact - - - -class QualifyLeadFunction(CoreDataverseFunction): - - - - def PreValidation(self, ctx: FunctionContext) -> None: - - """Check whether the lead meets qualification criteria.""" - - - - # SELECT — fetch the incoming lead by id - - # Conditions use typed field operators; no raw SQL strings - - result = ctx.dataverse.sql( - - select(Lead.leadid, Lead.companyname, Lead.budgetamount, - - Lead.firstname, Lead.lastname, Lead.emailaddress1) - - .from_(Lead) - - .where(Lead.leadid == ctx.input["leadid"]) - - .where(Lead.statecode == 0) # open leads only - - ).execute() - - - - lead = result.first() - - if lead is None: - - raise ValueError(f"Lead {ctx.input['leadid']} not found or already closed.") - - - - # Fail fast if qualification criteria are not met - - if lead.budgetamount is None or lead.budgetamount < 10_000: - - raise ValueError( - - f"Lead {lead.leadid} does not meet budget threshold " - - f"(budget={lead.budgetamount})." - - ) - - - - # Pass the qualified lead to subsequent stages via shared context - - ctx.shared["lead"] = lead - - - - def PreOperation(self, ctx: FunctionContext) -> None: - - """Update lead status to Qualified.""" - - - - lead = ctx.shared["lead"] - - - - ctx.dataverse.sql( - - update(Lead) - - .set(Lead.statecode, 2) # Qualified - - .set(Lead.statuscode, 3) # Qualified reason - - .where(Lead.leadid == lead.leadid) - - ).execute() - - - - def PostOperation(self, ctx: FunctionContext) -> None: - - """Create Account and Contact from the qualified lead.""" - - - - lead = ctx.shared["lead"] - - - - # INSERT Account — returns id of the created record - - account_result = ctx.dataverse.sql( - - insert_into(Account) - - .value(Account.name, lead.companyname) - - .value(Account.originatingleadid, lead.leadid) - - ).execute() - - - - account_id = account_result.id # GUID of the new Account record - - - - # INSERT Contact linked to the new Account - - ctx.dataverse.sql( - - insert_into(Contact) - - .value(Contact.firstname, lead.firstname) - - .value(Contact.lastname, lead.lastname) - - .value(Contact.emailaddress1, lead.emailaddress1) - - .value(Contact.accountid, account_id) - - ).execute() - - - - ctx.output["account_id"] = account_id - -Entity Type Generator - -The SDK exposes a generate() function that connects to a live Dataverse environment, downloads entity metadata via the OData Web API, and writes strongly typed Python classes to a Types/ folder inside the workspace. - -Programmatic usage - -from pathlib import Path - -from azure.identity import InteractiveBrowserCredential - -from generator import generate - - - -generate( - - org_url = "https://yourorg.crm.dynamics.com", - - entities = ["account", "contact", "lead"], - - credential = InteractiveBrowserCredential(), - - output_dir = Path("src/runtime/python/sdk"), - -) - -CLI usage - -python tools/generate_entity.py \ - - --url https://yourorg.crm.dynamics.com \ - - --entities account contact lead - -The generator also automatically discovers and fetches dependencies: - -Lookup dependencies — entities referenced by Lookup fields (e.g. businessunit) fetched automatically - -M2M participants — both sides of many-to-many relationships fetched automatically - -OneToMany referencers — entities that have a Lookup back to this entity opt-in prompt - - - -Generated output structure - -sdk/Types/ - - account.py — Account(Entity) with typed fields - - contact.py - - lead.py - - picklists/ - - lead_leadsourcecode.py — local option set - - .py — global option set shared across entities - - __init__.py - - booleans/ - - .py — boolean (two-option) types - - __init__.py - - intersects/ - - .py — M2M intersect entity stubs - - __init__.py - - _auto_types.py — fallback descriptors for unknown attribute types - - __init__.py - - - -Python Type representation of the System User Entity - -The image displays a segment of a code file, specifically a Python class definition for a 'Systemuser' entity in a data model, with attributes such as `guid`, `accessmode`, `applicationid`, `azureactivedirectoryobjectid`, `azurestate`, and `businessunitid`. - -AI-generated content may be incorrect. - -Core Field Types - -Every field in a generated entity class is a typed descriptor backed by a core SDK type. The table below maps Dataverse attribute types to their SDK equivalents and Python value types. - -SDK Type - -Dataverse Attribute Type(s) - -Python Value Type - -Text - -String, File, Image - -str - -Memo - -Memo - -str - -Integer - -Integer - -int - -BigInt - -BigInt - -int - -DecimalNumber - -Decimal - -float - -Double - -Double - -float - -Money - -Money - -float - -DateTime - -DateTime - -str (ISO 8601) - -Guid - -Uniqueidentifier - -str (UUID) - -Lookup - -Lookup, Owner - -str (entity id) - -CustomerLookup - -Customer (polymorphic) - -str (entity id) - -Boolean - -Boolean (with named options) - -bool - -BooleanBase - -Boolean (no named options) - -bool - -Picklist - -Picklist - -int - -State - -State - -int - -Status - -Status - -int - -MultiPicklist - -Multiselectpicklist, PartyList - -str - - - -AI Agent Persona/Workflow - -A customer begins by creating a new Dataverse workspace using the DV Plugin/CLI with a business intent. For example: - -Evaluate whether an incoming Lead meets qualification criteria and, if so, update the Lead status and create the corresponding Account and Contact records. - -The AI agent then: - -Validates the availability of required Dataverse entities within the SDK - -Connects to the live Dataverse organization and generates strongly typed Python classes aligned to the schema — including fields, relationships, and option sets - -Retrieves the entity model - -python tools/generate_entity.py \ - - --url https://.crm.dynamics.com \ - - --entities account contact lead - -Represents each entity as a first-class Python type with schema-aligned attributes - -Uses these generated types to author the required server-side business logic - -The resulting function and dependent entities are packaged and deployed to Dataverse, where they are stored in the sdkmessageprocessingfunction entity analogous to how plug-ins are registered today via sdkmessageprocessingstep and executed securely within the Dataverse sandbox Python runtime. \ No newline at end of file diff --git a/functional_testing_log.txt b/functional_testing_log.txt deleted file mode 100644 index c449a41b..00000000 --- a/functional_testing_log.txt +++ /dev/null @@ -1,191 +0,0 @@ -PowerPlatform Dataverse Client SDK - Advanced Functional Testing -====================================================================== -This script tests SDK functionality in a real Dataverse environment: - - Authentication & Connection - - Table Creation & Metadata Operations - - Record CRUD Operations - - Query Functionality - - Relationship Operations (1:N, N:N, lookup, get, delete) - - Batch Operations (create, read, update, changeset, delete) - - Interactive Cleanup -====================================================================== -For installation validation, run examples/basic/installation_example.py first -====================================================================== - --> Authentication Setup -================================================== - --> Dataverse Environment Setup -================================================== -Testing connection... -C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\msal\oauth2cli\oauth2.py:407: UserWarning: response_mode='form_post' is recommended for better security. See https://www.rfc-editor.org/rfc/rfc9700.html#section-4.3.1 - warnings.warn( -[OK] Connection successful! Found 1556 tables. -[OK] Found 866 user-owned tables (filter + select). - --> Test Table Setup -================================================== -Creating new test table... -[OK] Created test table: test_TestSDKFunctionality - Logical name: test_testsdkfunctionality - Entity set: test_testsdkfunctionalities - --> Record Creation Test -================================================== -Creating test record... -[OK] Record created successfully! - Record ID: 11179b49-1d53-f111-a821-00224808976e - Name: Test Record 17:54:33 - --> Record Reading Test -================================================== -Reading record: 11179b49-1d53-f111-a821-00224808976e -[OK] Record retrieved successfully! - Retrieved data: - test_name: Test Record 17:54:33 - test_description: This is a test record created by the SDK functionality test - test_count: 42 - test_amount: 123.45 - test_is_active: True -[OK] include_annotations verified: test_is_active@OData.Community.Display.V1.FormattedValue = 'True' -[OK] records.retrieve with expand=['owninguser']: owner='Aurora365 User1' - --> Record Query Test -================================================== -Querying records with records.list()... - Record 1: Test Record 17:54:33 (Count: 42, Amount: 123.45) -[OK] records.list() completed! Found 1 active records. - -Querying records with records.list_pages() (paged)... - Page 1: 1 record(s) — ['Test Record 17:54:33'] -[OK] records.list_pages() completed! 1 records across 1 page(s). - -Querying records.list() with orderby / page_size / count / include_annotations... - Records (ordered): ['Test Record 17:54:33'] -[OK] include_annotations verified: 'test_is_active@OData.Community.Display.V1.FormattedValue' present in list() results -[OK] records.list() with extended params completed! 1 record(s). - -Querying records.list_pages() with orderby / page_size / include_annotations... -[OK] include_annotations verified in list_pages() results -[OK] records.list_pages() with extended params completed! 1 record(s). - --> Relationship Tests -================================================== -Checking for leftover relationship test resources... - -Creating relationship test tables... -[OK] Created parent table: test_RelParent -[OK] Created child table: test_RelChild -[OK] Created M:N table: test_RelProject - - Test 1: Create 1:N relationship (core API) - --------------------------------------------- - [OK] Created 1:N relationship: test_RelParent_RelChild - Lookup: test_ParentId - ID: d5abd075-1d53-f111-a821-00224808976e - - Test 2: Create lookup field (convenience API) - --------------------------------------------- - [OK] Created lookup: test_ManagerId - Relationship: contact_test_relchild_test_ManagerId - - Test 3: Create N:N relationship - --------------------------------------------- - [OK] Created N:N relationship: test_relchild_relproject - ID: 9f10f78a-1d53-f111-a821-00224808976e - - Test 4: Query relationship metadata - --------------------------------------------- - [OK] Retrieved 1:N: test_RelParent_RelChild - Referenced: test_relparent - Referencing: test_relchild - [OK] Retrieved N:N: test_relchild_relproject - Entity1: test_relchild - Entity2: test_relproject - [OK] Non-existent relationship returns None - - Test 5: Delete relationships - --------------------------------------------- - [OK] Deleted 1:N relationship - [OK] Deleted lookup relationship - [OK] Deleted N:N relationship - [OK] Verified 1:N deletion (get returns None) - -[OK] All relationship tests passed! - (Cleaned up table: test_RelProject) - (Cleaned up table: test_RelChild) - (Cleaned up table: test_RelParent) - --> SQL Encoding Verification Test -================================================== - [1/3] Basic SELECT (no special chars): SELECT TOP 5 test_name FROM test_testsdkfunctionality - [OK] Both paths returned 1 rows - [2/3] WHERE with spaces/colons: ...WHERE test_name = 'Test Record 17:54:33' - [OK] Both paths found the record: 'Test Record 17:54:33' - [3/3] WHERE with '=' in string literal (tests %3D encoding) - [OK] Both paths found record with '=' in name: 'SQL=Test 18:00:34' -[OK] SQL encoding verification passed — %20/%3D encoding is consistent across both paths - --> Batch Operations Test (All Operations) -================================================== - -[1/11] Create — single + CreateMultiple (2 ops, 1 POST $batch) -[OK] 2 ops → 1 records created: ['4fc0b923-1e53-f111-a821-00224808976e'] - -[2/11] Read — records.retrieve + records.list(orderby/page_size/count/include_annotations) + tables.get + tables.list + query.sql (5 ops, 1 POST $batch) -[OK] 5 succeeded, 0 failed - records.retrieve → name='Batch-A 18:00:38', test_is_active@OData.Community.Display.V1.FormattedValue='True' - records.retrieve expand=['owninguser'] → owner='Aurora365 User1' - records.list → 4 row(s), ordered: ['Batch-A 18:00:38', 'Batch-B 18:00:38', 'Batch-C 18:00:38', 'Test Record 17:54:33'] - [OK] include_annotations 'test_is_active@OData.Community.Display.V1.FormattedValue' present in batch.records.list() results - tables.get → LogicalName='None', EntitySet='None' - tables.list → 1557 tables returned - query.sql → 4 rows returned - -[4/11] Changeset (happy path) — cs.create + cs.update(ref) + cs.delete (1 transaction) -[OK] 3 ops committed atomically (create + update + delete) - -[5/11] Changeset (rollback) — cs.create + cs.update(nonexistent) → full rollback -[OK] Changeset rollback verified: changeset failed, no records created - -[6/11] Two changesets in one batch — globally unique Content-IDs across changesets -[OK] Both changesets committed — 4 records created with globally unique Content-IDs across changesets: ['7da0a52a-1e53-f111-a821-00224808976e', '7da0a52a-1e53-f111-a821-00224808976e', '7ea0a52a-1e53-f111-a821-00224808976e', '7ea0a52a-1e53-f111-a821-00224808976e'] - -[7/11] Content-ID reference chaining — two creates + two updates via $n refs -[OK] Both records created and updated via content-ID refs $1 and $2: ['7fa0a52a-1e53-f111-a821-00224808976e', '80a0a52a-1e53-f111-a821-00224808976e', '7fa0a52a-1e53-f111-a821-00224808976e', '80a0a52a-1e53-f111-a821-00224808976e'] - -[8/11] Batch tables.add_columns — two add-column requests in one batch -[OK] 2 column(s) added via batch: test_batch_extra_a, test_batch_extra_b -[OK] Removed 2 batch-added column(s) via batch.tables.remove_columns - -[9/11] Upsert — UpsertItem with alternate key (expected to fail: no alt key on test table) -[WARN] Upsert failed as expected (no alternate key configured): 400 - -[10/11] Mixed batch (continue_on_error=True) — 1 bad get + 1 good get -examples/basic/functional_testing.py:874: DeprecationWarning: 'batch.records.get()' is deprecated; use 'batch.records.retrieve(table, record_id)' instead. - batch.records.get( -examples/basic/functional_testing.py:879: DeprecationWarning: 'batch.records.get()' is deprecated; use 'batch.records.retrieve(table, record_id)' instead. - batch.records.get( -[OK] Succeeded: 1, Failed: 1 - Expected failure: 404 Entity 'test_testsdkfunctionality' With Id = 00000000-0000-0000-0000-000000000002 Does Not Exist - -[11/11] Delete — 9 records via multi-delete (use_bulk_delete=False, 1 POST $batch) -[OK] Deleted 5, failed 4 - -[OK] Batch all-operations test completed! - -Functional Test Summary -================================================== -[OK] Authentication: Success -[OK] Table Operations: Success -[OK] Record Creation: Success -[OK] Record Reading: Success -[OK] Record Querying: Success -[OK] Relationship Operations: Success -[OK] SQL Encoding: Success -[OK] Batch Operations: Success - -Your PowerPlatform Dataverse Client SDK is fully functional! - --> Cleanup -================================================== diff --git a/functional_testing_output.txt b/functional_testing_output.txt deleted file mode 100644 index 59a99c86..00000000 --- a/functional_testing_output.txt +++ /dev/null @@ -1,24 +0,0 @@ -********************** -PowerShell transcript start -Start time: 20260518173652 -Username: REDMOND\abelmilash -RunAs User: REDMOND\abelmilash -Configuration Name: -Machine: LAPTOP-TTK1RHIJ (Microsoft Windows NT 10.0.26100.0) -Host Application: C:\Program Files\WindowsApps\Microsoft.PowerShell_7.6.1.0_x64__8wekyb3d8bbwe\pwsh.dll -noexit -command try { . "c:\Users\abelmilash\AppData\Local\Programs\Microsoft VS Code\07ff9d6178\resources\app\out\vs\workbench\contrib\terminal\common\scripts\shellIntegration.ps1" } catch {} -Process ID: 25756 -PSVersion: 7.6.1 -PSEdition: Core -GitCommitId: 7.6.1 -OS: Microsoft Windows 10.0.26100 -Platform: Win32NT -PSCompatibleVersions: 1.0, 2.0, 3.0, 4.0, 5.0, 5.1, 6.0, 7.0 -PSRemotingProtocolVersion: 2.4 -SerializationVersion: 1.1.0.1 -WSManStackVersion: 3.0 -********************** -Transcript started, output file is C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\functional_testing_output.txt -[OK] Test record deleted successfully -Do you want to delete the test table? (y/N): y -[OK] Test table deleted successfully -]633;D;0]633;A]633;P;Cwd=C:\x5cUsers\x5cabelmilash\x5cRepos\x5cPowerPlatform-DataverseClient-PythonPS C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python> ]633;B diff --git a/logs/log.txt b/logs/log.txt deleted file mode 100644 index ba3155c9..00000000 --- a/logs/log.txt +++ /dev/null @@ -1,245 +0,0 @@ -INFO - __main__ - Adding yaml extension to path -INFO - py2docfx.convert_prepare.environment - : Creating basevenv... -INFO - py2docfx.convert_prepare.environment - : Installing converter requirements in ... -INFO - py2docfx.convert_prepare.pip_utils - Collecting setuptools<81 - Downloading setuptools-80.10.2-py3-none-any.whl.metadata (6.6 kB) -Collecting wheel - Using cached wheel-0.47.0-py3-none-any.whl.metadata (2.3 kB) -Collecting vcs-versioning - Downloading vcs_versioning-1.1.1-py3-none-any.whl.metadata (1.6 kB) -Collecting packaging>=24.0 (from wheel) - Using cached packaging-26.2-py3-none-any.whl.metadata (3.5 kB) -Downloading setuptools-80.10.2-py3-none-any.whl (1.1 MB) - ---------------------------------------- 1.1/1.1 MB 2.9 MB/s eta 0:00:00 -Using cached wheel-0.47.0-py3-none-any.whl (32 kB) -Downloading vcs_versioning-1.1.1-py3-none-any.whl (79 kB) -Using cached packaging-26.2-py3-none-any.whl (100 kB) -Installing collected packages: setuptools, packaging, wheel, vcs-versioning -Successfully installed packaging-26.2 setuptools-80.10.2 vcs-versioning-1.1.1 wheel-0.47.0 - -INFO - py2docfx.convert_prepare.git - : Cloning repo: .... -INFO - py2docfx.convert_prepare.git - Using empty branch of ., going to use master branch instead. -INFO - py2docfx.convert_prepare.git - Using master branch of ., going to use main branch instead. -INFO - py2docfx.convert_prepare.git - : Repo . successfully cloned in C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0... -INFO - py2docfx.convert_prepare.environment - : Installing required packages in basevenv... -INFO - py2docfx.convert_prepare.environment - : basevenv setup complete. -INFO - __main__ - Processing package PowerPlatform-Dataverse-Client, env_prepare_tasks: 1 -INFO - py2docfx.convert_prepare.pip_utils - Processing c:\users\abelmilash\repos\powerplatform-dataverseclient-python\.conda\lib\site-packages\py2docfx\source_repo\0 - Installing build dependencies: started - Installing build dependencies: finished with status 'done' - Getting requirements to build wheel: started - Getting requirements to build wheel: finished with status 'done' - Preparing metadata (pyproject.toml): started - Preparing metadata (pyproject.toml): finished with status 'done' -Collecting azure-identity>=1.17.0 (from PowerPlatform-Dataverse-Client==0.1.0b11) - Using cached azure_identity-1.25.3-py3-none-any.whl.metadata (91 kB) -Collecting azure-core>=1.30.2 (from PowerPlatform-Dataverse-Client==0.1.0b11) - Using cached azure_core-1.41.0-py3-none-any.whl.metadata (49 kB) -Collecting requests>=2.32.0 (from PowerPlatform-Dataverse-Client==0.1.0b11) - Using cached requests-2.34.2-py3-none-any.whl.metadata (4.8 kB) -Collecting pandas>=2.0.0 (from PowerPlatform-Dataverse-Client==0.1.0b11) - Using cached pandas-3.0.3-cp312-cp312-win_amd64.whl.metadata (19 kB) -Collecting typing-extensions>=4.6.0 (from azure-core>=1.30.2->PowerPlatform-Dataverse-Client==0.1.0b11) - Using cached typing_extensions-4.15.0-py3-none-any.whl.metadata (3.3 kB) -Collecting cryptography>=2.5 (from azure-identity>=1.17.0->PowerPlatform-Dataverse-Client==0.1.0b11) - Using cached cryptography-48.0.0-cp311-abi3-win_amd64.whl.metadata (4.3 kB) -Collecting msal>=1.35.1 (from azure-identity>=1.17.0->PowerPlatform-Dataverse-Client==0.1.0b11) - Using cached msal-1.36.0-py3-none-any.whl.metadata (11 kB) -Collecting msal-extensions>=1.2.0 (from azure-identity>=1.17.0->PowerPlatform-Dataverse-Client==0.1.0b11) - Using cached msal_extensions-1.3.1-py3-none-any.whl.metadata (7.8 kB) -Collecting numpy>=1.26.0 (from pandas>=2.0.0->PowerPlatform-Dataverse-Client==0.1.0b11) - Using cached numpy-2.4.6-cp312-cp312-win_amd64.whl.metadata (6.6 kB) -Collecting python-dateutil>=2.8.2 (from pandas>=2.0.0->PowerPlatform-Dataverse-Client==0.1.0b11) - Using cached python_dateutil-2.9.0.post0-py2.py3-none-any.whl.metadata (8.4 kB) -Collecting tzdata (from pandas>=2.0.0->PowerPlatform-Dataverse-Client==0.1.0b11) - Using cached tzdata-2026.2-py2.py3-none-any.whl.metadata (1.4 kB) -Collecting charset_normalizer<4,>=2 (from requests>=2.32.0->PowerPlatform-Dataverse-Client==0.1.0b11) - Using cached charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl.metadata (41 kB) -Collecting idna<4,>=2.5 (from requests>=2.32.0->PowerPlatform-Dataverse-Client==0.1.0b11) - Using cached idna-3.15-py3-none-any.whl.metadata (7.7 kB) -Collecting urllib3<3,>=1.26 (from requests>=2.32.0->PowerPlatform-Dataverse-Client==0.1.0b11) - Using cached urllib3-2.7.0-py3-none-any.whl.metadata (6.9 kB) -Collecting certifi>=2023.5.7 (from requests>=2.32.0->PowerPlatform-Dataverse-Client==0.1.0b11) - Using cached certifi-2026.5.20-py3-none-any.whl.metadata (2.5 kB) -Collecting cffi>=2.0.0 (from cryptography>=2.5->azure-identity>=1.17.0->PowerPlatform-Dataverse-Client==0.1.0b11) - Using cached cffi-2.0.0-cp312-cp312-win_amd64.whl.metadata (2.6 kB) -Collecting PyJWT<3,>=1.0.0 (from PyJWT[crypto]<3,>=1.0.0->msal>=1.35.1->azure-identity>=1.17.0->PowerPlatform-Dataverse-Client==0.1.0b11) - Using cached pyjwt-2.12.1-py3-none-any.whl.metadata (4.1 kB) -Collecting six>=1.5 (from python-dateutil>=2.8.2->pandas>=2.0.0->PowerPlatform-Dataverse-Client==0.1.0b11) - Using cached six-1.17.0-py2.py3-none-any.whl.metadata (1.7 kB) -Collecting pycparser (from cffi>=2.0.0->cryptography>=2.5->azure-identity>=1.17.0->PowerPlatform-Dataverse-Client==0.1.0b11) - Using cached pycparser-3.0-py3-none-any.whl.metadata (8.2 kB) -Using cached azure_core-1.41.0-py3-none-any.whl (220 kB) -Using cached azure_identity-1.25.3-py3-none-any.whl (192 kB) -Using cached pandas-3.0.3-cp312-cp312-win_amd64.whl (9.8 MB) -Using cached requests-2.34.2-py3-none-any.whl (73 kB) -Using cached certifi-2026.5.20-py3-none-any.whl (134 kB) -Using cached charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl (159 kB) -Using cached cryptography-48.0.0-cp311-abi3-win_amd64.whl (3.8 MB) -Using cached idna-3.15-py3-none-any.whl (72 kB) -Using cached msal-1.36.0-py3-none-any.whl (121 kB) -Using cached msal_extensions-1.3.1-py3-none-any.whl (20 kB) -Using cached numpy-2.4.6-cp312-cp312-win_amd64.whl (12.3 MB) -Using cached python_dateutil-2.9.0.post0-py2.py3-none-any.whl (229 kB) -Using cached typing_extensions-4.15.0-py3-none-any.whl (44 kB) -Using cached urllib3-2.7.0-py3-none-any.whl (131 kB) -Using cached tzdata-2026.2-py2.py3-none-any.whl (349 kB) -Using cached cffi-2.0.0-cp312-cp312-win_amd64.whl (183 kB) -Using cached pyjwt-2.12.1-py3-none-any.whl (29 kB) -Using cached six-1.17.0-py2.py3-none-any.whl (11 kB) -Using cached pycparser-3.0-py3-none-any.whl (48 kB) -Building wheels for collected packages: PowerPlatform-Dataverse-Client - Building wheel for PowerPlatform-Dataverse-Client (pyproject.toml): started - Building wheel for PowerPlatform-Dataverse-Client (pyproject.toml): finished with status 'done' - Created wheel for PowerPlatform-Dataverse-Client: filename=powerplatform_dataverse_client-0.1.0b11-py3-none-any.whl size=161972 sha256=733aa96924b44326771251c46d77ecca31ad94f11165fd1b3e50004d5f728b8e - Stored in directory: C:\Users\abelmilash\AppData\Local\Temp\pip-ephem-wheel-cache-oqd6t93_\wheels\62\fb\bd\36436e7b52d8c20c8ed21d1a9ebe6261f7ddca751551c08661 -Successfully built PowerPlatform-Dataverse-Client -Installing collected packages: urllib3, tzdata, typing-extensions, six, PyJWT, pycparser, numpy, idna, charset_normalizer, certifi, requests, python-dateutil, cffi, pandas, cryptography, azure-core, msal, msal-extensions, azure-identity, PowerPlatform-Dataverse-Client -Successfully installed PowerPlatform-Dataverse-Client-0.1.0b11 PyJWT-2.12.1 azure-core-1.41.0 azure-identity-1.25.3 certifi-2026.5.20 cffi-2.0.0 charset_normalizer-3.4.7 cryptography-48.0.0 idna-3.15 msal-1.36.0 msal-extensions-1.3.1 numpy-2.4.6 pandas-3.0.3 pycparser-3.0 python-dateutil-2.9.0.post0 requests-2.34.2 six-1.17.0 typing-extensions-4.15.0 tzdata-2026.2 urllib3-2.7.0 - -INFO - py2docfx.convert_prepare.environment - : venv0 setup complete. -INFO - __main__ - Adding yaml extension to path -INFO - py2docfx.convert_prepare.environment - : Creating basevenv... -INFO - py2docfx.convert_prepare.environment - : Installing converter requirements in ... -INFO - py2docfx.convert_prepare.pip_utils - Collecting setuptools<81 - Using cached setuptools-80.10.2-py3-none-any.whl.metadata (6.6 kB) -Collecting wheel - Using cached wheel-0.47.0-py3-none-any.whl.metadata (2.3 kB) -Collecting vcs-versioning - Using cached vcs_versioning-1.1.1-py3-none-any.whl.metadata (1.6 kB) -Collecting packaging>=24.0 (from wheel) - Using cached packaging-26.2-py3-none-any.whl.metadata (3.5 kB) -Using cached setuptools-80.10.2-py3-none-any.whl (1.1 MB) -Using cached wheel-0.47.0-py3-none-any.whl (32 kB) -Using cached vcs_versioning-1.1.1-py3-none-any.whl (79 kB) -Using cached packaging-26.2-py3-none-any.whl (100 kB) -Installing collected packages: setuptools, packaging, wheel, vcs-versioning -Successfully installed packaging-26.2 setuptools-80.10.2 vcs-versioning-1.1.1 wheel-0.47.0 - -ERROR - py2docfx.convert_prepare.pip_utils - Pip download failed with a non-retriable error. Exit code: 2. -ERROR - py2docfx.convert_prepare.pip_utils - STDOUT: Processing c:\users\abelmilash\repos\powerplatform-dataverseclient-python\.conda\lib\site-packages\py2docfx\dist\powerplatform_dataverse_client-0.1.0b11-py3-none-any.whl - -ERROR - py2docfx.convert_prepare.pip_utils - STDERR: WARNING: Requirement 'dist/powerplatform_dataverse_client-0.1.0b11-py3-none-any.whl' looks like a filename, but the file does not exist -ERROR: Exception: -Traceback (most recent call last): - File "C:\Users\abelmilash\AppData\Local\anaconda3\Lib\site-packages\pip\_internal\cli\base_command.py", line 107, in _run_wrapper - status = _inner_run() - File "C:\Users\abelmilash\AppData\Local\anaconda3\Lib\site-packages\pip\_internal\cli\base_command.py", line 98, in _inner_run - return self.run(options, args) - ~~~~~~~~^^^^^^^^^^^^^^^ - File "C:\Users\abelmilash\AppData\Local\anaconda3\Lib\site-packages\pip\_internal\cli\req_command.py", line 85, in wrapper - return func(self, options, args) - File "C:\Users\abelmilash\AppData\Local\anaconda3\Lib\site-packages\pip\_internal\commands\download.py", line 128, in run - requirement_set = resolver.resolve(reqs, check_supported_wheels=True) - File "C:\Users\abelmilash\AppData\Local\anaconda3\Lib\site-packages\pip\_internal\resolution\resolvelib\resolver.py", line 79, in resolve - collected = self.factory.collect_root_requirements(root_reqs) - File "C:\Users\abelmilash\AppData\Local\anaconda3\Lib\site-packages\pip\_internal\resolution\resolvelib\factory.py", line 538, in collect_root_requirements - reqs = list( - self._make_requirements_from_install_req( - ...<2 lines>... - ) - ) - File "C:\Users\abelmilash\AppData\Local\anaconda3\Lib\site-packages\pip\_internal\resolution\resolvelib\factory.py", line 494, in _make_requirements_from_install_req - cand = self._make_base_candidate_from_link( - ireq.link, - ...<2 lines>... - version=None, - ) - File "C:\Users\abelmilash\AppData\Local\anaconda3\Lib\site-packages\pip\_internal\resolution\resolvelib\factory.py", line 226, in _make_base_candidate_from_link - self._link_candidate_cache[link] = LinkCandidate( - ~~~~~~~~~~~~~^ - link, - ^^^^^ - ...<3 lines>... - version=version, - ^^^^^^^^^^^^^^^^ - ) - ^ - File "C:\Users\abelmilash\AppData\Local\anaconda3\Lib\site-packages\pip\_internal\resolution\resolvelib\candidates.py", line 318, in __init__ - super().__init__( - ~~~~~~~~~~~~~~~~^ - link=link, - ^^^^^^^^^^ - ...<4 lines>... - version=version, - ^^^^^^^^^^^^^^^^ - ) - ^ - File "C:\Users\abelmilash\AppData\Local\anaconda3\Lib\site-packages\pip\_internal\resolution\resolvelib\candidates.py", line 161, in __init__ - self.dist = self._prepare() - ~~~~~~~~~~~~~^^ - File "C:\Users\abelmilash\AppData\Local\anaconda3\Lib\site-packages\pip\_internal\resolution\resolvelib\candidates.py", line 238, in _prepare - dist = self._prepare_distribution() - File "C:\Users\abelmilash\AppData\Local\anaconda3\Lib\site-packages\pip\_internal\resolution\resolvelib\candidates.py", line 329, in _prepare_distribution - return preparer.prepare_linked_requirement(self._ireq, parallel_builds=True) - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\abelmilash\AppData\Local\anaconda3\Lib\site-packages\pip\_internal\operations\prepare.py", line 543, in prepare_linked_requirement - return self._prepare_linked_requirement(req, parallel_builds) - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\abelmilash\AppData\Local\anaconda3\Lib\site-packages\pip\_internal\operations\prepare.py", line 648, in _prepare_linked_requirement - hash = hash_file(local_file.path)[0].hexdigest() - ~~~~~~~~~^^^^^^^^^^^^^^^^^ - File "C:\Users\abelmilash\AppData\Local\anaconda3\Lib\site-packages\pip\_internal\utils\misc.py", line 614, in hash_file - with open(path, "rb") as f: - ~~~~^^^^^^^^^^^^ -FileNotFoundError: [Errno 2] No such file or directory: 'C:\\Users\\abelmilash\\Repos\\PowerPlatform-DataverseClient-Python\\.conda\\Lib\\site-packages\\py2docfx\\dist\\powerplatform_dataverse_client-0.1.0b11-py3-none-any.whl' - -INFO - py2docfx.convert_prepare.environment - : Installing required packages in basevenv... -INFO - py2docfx.convert_prepare.environment - : basevenv setup complete. -INFO - __main__ - Processing package None, env_prepare_tasks: 1 -ERROR - __main__ - Failed to setup venv for package None: Command '['pip', 'download', '--dest', 'C:\\Users\\abelmilash\\Repos\\PowerPlatform-DataverseClient-Python\\.conda\\Lib\\site-packages\\py2docfx\\dist_temp\\0', '--no-deps', 'dist/powerplatform_dataverse_client-0.1.0b11-py3-none-any.whl', '--prefer-binary']' returned non-zero exit status 2. -ERROR - __main__ - An error occurred: Command '['pip', 'download', '--dest', 'C:\\Users\\abelmilash\\Repos\\PowerPlatform-DataverseClient-Python\\.conda\\Lib\\site-packages\\py2docfx\\dist_temp\\0', '--no-deps', 'dist/powerplatform_dataverse_client-0.1.0b11-py3-none-any.whl', '--prefer-binary']' returned non-zero exit status 2. -INFO - __main__ - Adding yaml extension to path -INFO - py2docfx.convert_prepare.environment - : Creating basevenv... -INFO - py2docfx.convert_prepare.environment - : Installing converter requirements in ... -INFO - py2docfx.convert_prepare.pip_utils - Collecting setuptools<81 - Using cached setuptools-80.10.2-py3-none-any.whl.metadata (6.6 kB) -Collecting wheel - Using cached wheel-0.47.0-py3-none-any.whl.metadata (2.3 kB) -Collecting vcs-versioning - Using cached vcs_versioning-1.1.1-py3-none-any.whl.metadata (1.6 kB) -Collecting packaging>=24.0 (from wheel) - Using cached packaging-26.2-py3-none-any.whl.metadata (3.5 kB) -Using cached setuptools-80.10.2-py3-none-any.whl (1.1 MB) -Using cached wheel-0.47.0-py3-none-any.whl (32 kB) -Using cached vcs_versioning-1.1.1-py3-none-any.whl (79 kB) -Using cached packaging-26.2-py3-none-any.whl (100 kB) -Installing collected packages: setuptools, packaging, wheel, vcs-versioning -Successfully installed packaging-26.2 setuptools-80.10.2 vcs-versioning-1.1.1 wheel-0.47.0 - -INFO - py2docfx.convert_prepare.pip_utils - Processing c:\users\abelmilash\repos\powerplatform-dataverseclient-python\dist\powerplatform_dataverse_client-0.1.0b11-py3-none-any.whl -Saved c:\users\abelmilash\repos\powerplatform-dataverseclient-python\.conda\lib\site-packages\py2docfx\dist_temp\0\powerplatform_dataverse_client-0.1.0b11-py3-none-any.whl -Successfully downloaded powerplatform-dataverse-client - -INFO - py2docfx.convert_prepare.environment - : Installing required packages in basevenv... -INFO - py2docfx.convert_prepare.environment - : basevenv setup complete. -INFO - __main__ - Processing package None, env_prepare_tasks: 1 -ERROR - __main__ - Failed to setup venv for package None: join() argument must be str, bytes, or os.PathLike object, not 'NoneType' -ERROR - __main__ - An error occurred: join() argument must be str, bytes, or os.PathLike object, not 'NoneType' -INFO - __main__ - Adding yaml extension to path -INFO - py2docfx.convert_prepare.environment - : Creating basevenv... -INFO - py2docfx.convert_prepare.environment - : Installing converter requirements in ... -INFO - py2docfx.convert_prepare.pip_utils - Collecting setuptools<81 - Using cached setuptools-80.10.2-py3-none-any.whl.metadata (6.6 kB) -Collecting wheel - Using cached wheel-0.47.0-py3-none-any.whl.metadata (2.3 kB) -Collecting vcs-versioning - Using cached vcs_versioning-1.1.1-py3-none-any.whl.metadata (1.6 kB) -Collecting packaging>=24.0 (from wheel) - Using cached packaging-26.2-py3-none-any.whl.metadata (3.5 kB) -Using cached setuptools-80.10.2-py3-none-any.whl (1.1 MB) -Using cached wheel-0.47.0-py3-none-any.whl (32 kB) -Using cached vcs_versioning-1.1.1-py3-none-any.whl (79 kB) -Using cached packaging-26.2-py3-none-any.whl (100 kB) -Installing collected packages: setuptools, packaging, wheel, vcs-versioning -Successfully installed packaging-26.2 setuptools-80.10.2 vcs-versioning-1.1.1 wheel-0.47.0 - -INFO - py2docfx.convert_prepare.pip_utils - Processing c:\users\abelmilash\repos\powerplatform-dataverseclient-python\dist\powerplatform_dataverse_client-0.1.0b11-py3-none-any.whl -Saved c:\users\abelmilash\repos\powerplatform-dataverseclient-python\.conda\lib\site-packages\py2docfx\dist_temp\0\powerplatform_dataverse_client-0.1.0b11-py3-none-any.whl -Successfully downloaded powerplatform-dataverse-client - -INFO - py2docfx.convert_prepare.environment - : Installing required packages in basevenv... -INFO - py2docfx.convert_prepare.environment - : basevenv setup complete. -INFO - __main__ - Processing package None, env_prepare_tasks: 1 -ERROR - __main__ - Failed to setup venv for package None: join() argument must be str, bytes, or os.PathLike object, not 'NoneType' -ERROR - __main__ - An error occurred: join() argument must be str, bytes, or os.PathLike object, not 'NoneType' diff --git a/logs/package_logs/None.txt b/logs/package_logs/None.txt deleted file mode 100644 index 1c60d257..00000000 --- a/logs/package_logs/None.txt +++ /dev/null @@ -1,4 +0,0 @@ -INFO - py2docfx.convert_prepare.pack - Unpacking to: C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\dist_temp\0\powerplatform_dataverse_client-0.1.0b11...OK - -INFO - py2docfx.convert_prepare.pack - Unpacking to: C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\dist_temp\0\powerplatform_dataverse_client-0.1.0b11...OK - diff --git a/logs/package_logs/PowerPlatform-Dataverse-Client.txt b/logs/package_logs/PowerPlatform-Dataverse-Client.txt deleted file mode 100644 index b5f9e8c1..00000000 --- a/logs/package_logs/PowerPlatform-Dataverse-Client.txt +++ /dev/null @@ -1,60 +0,0 @@ -INFO - py2docfx.convert_prepare.generate_document - : Generating RST files for PowerPlatform-Dataverse-Client. -INFO - py2docfx.convert_prepare.sphinx_caller - : Subfolder path C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\.azdo. -INFO - py2docfx.convert_prepare.sphinx_caller - : Subfolder path C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\.claude. -INFO - py2docfx.convert_prepare.sphinx_caller - : Subfolder path C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\.github. -INFO - py2docfx.convert_prepare.sphinx_caller - : Subfolder path C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\build. -INFO - py2docfx.convert_prepare.sphinx_caller - : Subfolder path C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc. -INFO - py2docfx.convert_prepare.sphinx_caller - : Subfolder path C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\examples. -INFO - py2docfx.convert_prepare.sphinx_caller - Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\examples.rst. -Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\examples.advanced.rst. -Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\examples.basic.rst. - -INFO - py2docfx.convert_prepare.sphinx_caller - : Subfolder path C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\src. -INFO - py2docfx.convert_prepare.sphinx_caller - Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\src.rst. -Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\src.PowerPlatform.rst. -Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\src.PowerPlatform.Dataverse.rst. -Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\src.PowerPlatform.Dataverse.claude_skill.rst. -Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\src.PowerPlatform.Dataverse.common.rst. -Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\src.PowerPlatform.Dataverse.core.rst. -Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\src.PowerPlatform.Dataverse.data.rst. -Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\src.PowerPlatform.Dataverse.extensions.rst. -Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\src.PowerPlatform.Dataverse.migration.rst. -Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\src.PowerPlatform.Dataverse.models.rst. -Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\src.PowerPlatform.Dataverse.operations.rst. -Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\src.PowerPlatform.Dataverse.utils.rst. - -INFO - py2docfx.convert_prepare.sphinx_caller - : Subfolder path C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\tests. -INFO - py2docfx.convert_prepare.sphinx_caller - Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\tests.rst. -Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\tests.e2e.rst. -Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\tests.fixtures.rst. -Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\tests.unit.rst. -Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\tests.unit.core.rst. -Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\tests.unit.data.rst. -Creating file C:\Users\abelmilash\Repos\PowerPlatform-DataverseClient-Python\.conda\Lib\site-packages\py2docfx\source_repo\0\doc\tests.unit.models.rst. - -INFO - py2docfx.convert_prepare.generate_document - : Listing RST files: -INFO - py2docfx.convert_prepare.generate_document - examples.advanced.rst -INFO - py2docfx.convert_prepare.generate_document - examples.basic.rst -INFO - py2docfx.convert_prepare.generate_document - examples.rst -INFO - py2docfx.convert_prepare.generate_document - src.PowerPlatform.Dataverse.claude_skill.rst -INFO - py2docfx.convert_prepare.generate_document - src.PowerPlatform.Dataverse.common.rst -INFO - py2docfx.convert_prepare.generate_document - src.PowerPlatform.Dataverse.core.rst -INFO - py2docfx.convert_prepare.generate_document - src.PowerPlatform.Dataverse.data.rst -INFO - py2docfx.convert_prepare.generate_document - src.PowerPlatform.Dataverse.extensions.rst -INFO - py2docfx.convert_prepare.generate_document - src.PowerPlatform.Dataverse.migration.rst -INFO - py2docfx.convert_prepare.generate_document - src.PowerPlatform.Dataverse.models.rst -INFO - py2docfx.convert_prepare.generate_document - src.PowerPlatform.Dataverse.operations.rst -INFO - py2docfx.convert_prepare.generate_document - src.PowerPlatform.Dataverse.rst -INFO - py2docfx.convert_prepare.generate_document - src.PowerPlatform.Dataverse.utils.rst -INFO - py2docfx.convert_prepare.generate_document - src.PowerPlatform.rst -INFO - py2docfx.convert_prepare.generate_document - src.rst -INFO - py2docfx.convert_prepare.generate_document - tests.e2e.rst -INFO - py2docfx.convert_prepare.generate_document - tests.fixtures.rst -INFO - py2docfx.convert_prepare.generate_document - tests.rst -INFO - py2docfx.convert_prepare.generate_document - tests.unit.core.rst -INFO - py2docfx.convert_prepare.generate_document - tests.unit.data.rst -INFO - py2docfx.convert_prepare.generate_document - tests.unit.models.rst -INFO - py2docfx.convert_prepare.generate_document - tests.unit.rst -INFO - py2docfx.convert_prepare.generate_document - : Running Sphinx build... -INFO - py2docfx.convert_prepare.sphinx_caller - b'Running Sphinx v6.1.3\r\nmaking output directory... done\r\nbuilding [mo]: targets for 0 po files that are out of date\r\nwriting output... \r\nbuilding [yaml]: targets for 23 source files that are out of date\r\nupdating environment: [new config] 23 added, 0 changed, 0 removed\r\nreading sources... [ 4%] examples \rreading sources... [ 8%] examples.advanced \rreading sources... [ 13%] examples.basic \rreading sources... [ 17%] index \rreading sources... [ 21%] src \rreading sources... [ 26%] src.PowerPlatform \rreading sources... [ 30%] src.PowerPlatform.Dataverse \rreading sources... [ 34%] src.PowerPlatform.Dataverse.claude_skill \rreading sources... [ 39%] src.PowerPlatform.Dataverse.common \rreading sources... [ 43%] src.PowerPlatform.Dataverse.core \rreading sources... [ 47%] src.PowerPlatform.Dataverse.data \rreading sources... [ 52%] src.PowerPlatform.Dataverse.extensions \rreading sources... [ 56%] src.PowerPlatform.Dataverse.migration \rreading sources... [ 60%] src.PowerPlatform.Dataverse.models \rreading sources... [ 65%] src.PowerPlatform.Dataverse.operations \rreading sources... [ 69%] src.PowerPlatform.Dataverse.utils \rreading sources... [ 73%] tests \rreading sources... [ 78%] tests.e2e \rreading sources... [ 82%] tests.fixtures \rreading sources... [ 86%] tests.unit \rreading sources... [ 91%] tests.unit.core \rreading sources... [ 95%] tests.unit.data \rreading sources... [100%] tests.unit.models \r\r\nlooking for now-outdated files... none found\r\npickling environment... done\r\nchecking consistency... done\r\npreparing documents... done\r\nwriting output... [ 4%] examples \rwriting output... [ 8%] examples.advanced \rwriting output... [ 13%] examples.basic \rwriting output... [ 17%] index \rwriting output... [ 21%] src \rwriting output... [ 26%] src.PowerPlatform \rwriting output... [ 30%] src.PowerPlatform.Dataverse \rwriting output... [ 34%] src.PowerPlatform.Dataverse.claude_skill \rwriting output... [ 39%] src.PowerPlatform.Dataverse.common \rwriting output... [ 43%] src.PowerPlatform.Dataverse.core \rwriting output... [ 47%] src.PowerPlatform.Dataverse.data \rwriting output... [ 52%] src.PowerPlatform.Dataverse.extensions \rwriting output... [ 56%] src.PowerPlatform.Dataverse.migration \rwriting output... [ 60%] src.PowerPlatform.Dataverse.models \rwriting output... [ 65%] src.PowerPlatform.Dataverse.operations \rwriting output... [ 69%] src.PowerPlatform.Dataverse.utils \rwriting output... [ 73%] tests \rwriting output... [ 78%] tests.e2e \rwriting output... [ 82%] tests.fixtures \rwriting output... [ 86%] tests.unit \rwriting output... [ 91%] tests.unit.core \rwriting output... [ 95%] tests.unit.data \rwriting output... [100%] tests.unit.models \r\r\nbuild succeeded, 100 warnings.\r\n' -INFO - py2docfx.convert_prepare.sphinx_caller - b"WARNING: autodoc: failed to import module 'examples'; the following exception was raised:\r\nNo module named 'examples'\r\nWARNING: autodoc: failed to import module 'advanced' from module 'examples'; the following exception was raised:\r\nNo module named 'examples'\r\nWARNING: autodoc: failed to import module 'advanced.alternate_keys_upsert' from module 'examples'; the following exception was raised:\r\nNo module named 'examples'\r\nWARNING: autodoc: failed to import module 'advanced.batch' from module 'examples'; the following exception was raised:\r\nNo module named 'examples'\r\nWARNING: autodoc: failed to import module 'advanced.dataframe_operations' from module 'examples'; the following exception was raised:\r\nNo module named 'examples'\r\nWARNING: autodoc: failed to import module 'advanced.datascience_risk_assessment' from module 'examples'; the following exception was raised:\r\nNo module named 'examples'\r\nWARNING: autodoc: failed to import module 'advanced.fetchxml' from module 'examples'; the following exception was raised:\r\nNo module named 'examples'\r\nWARNING: autodoc: failed to import module 'advanced.file_upload' from module 'examples'; the following exception was raised:\r\nNo module named 'examples'\r\nWARNING: autodoc: failed to import module 'advanced.prodev_quick_start' from module 'examples'; the following exception was raised:\r\nNo module named 'examples'\r\nWARNING: autodoc: failed to import module 'advanced.relationships' from module 'examples'; the following exception was raised:\r\nNo module named 'examples'\r\nWARNING: autodoc: failed to import module 'advanced.sql_examples' from module 'examples'; the following exception was raised:\r\nNo module named 'examples'\r\nWARNING: autodoc: failed to import module 'advanced.walkthrough' from module 'examples'; the following exception was raised:\r\nNo module named 'examples'\r\nWARNING: autodoc: failed to import module 'basic' from module 'examples'; the following exception was raised:\r\nNo module named 'examples'\r\nWARNING: autodoc: failed to import module 'basic.functional_testing' from module 'examples'; the following exception was raised:\r\nNo module named 'examples'\r\nWARNING: autodoc: failed to import module 'basic.installation_example' from module 'examples'; the following exception was raised:\r\nNo module named 'examples'\r\nWARNING: autodoc: failed to import module 'PowerPlatform' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.client' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.claude_skill' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.common' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.common.constants' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.core' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.core.config' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.core.errors' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.core.log_config' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.data' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.extensions' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.migration' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.migration.migrate_v0_to_v1' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.models' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.models.batch' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.models.fetchxml_query' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.models.filters' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.models.labels' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.models.protocol' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.models.query_builder' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.models.record' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.models.relationship' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.models.table_info' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.models.upsert' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.operations' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.operations.batch' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.operations.dataframe' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.operations.files' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.operations.query' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.operations.records' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.operations.tables' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'PowerPlatform.Dataverse.utils' from module 'src'; the following exception was raised:\r\nNo module named 'src'\r\nWARNING: autodoc: failed to import module 'e2e' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.e2e'\r\nWARNING: autodoc: failed to import module 'e2e.test_relationships_e2e' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.e2e'\r\nWARNING: autodoc: failed to import module 'fixtures.test_data' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.fixtures'\r\nWARNING: autodoc: failed to import module 'unit' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.test_batch_dataframe' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.test_batch_operations' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.test_batch_scenarios' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.test_client' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.test_client_dataframe' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.test_client_deprecations' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.test_context_manager' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.test_dataframe_operations' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.test_files_operations' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.test_migration_tool' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.test_operation_context' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.test_pandas_helpers' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.test_phase1_ga' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.test_phase2_ga' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.test_phase3_ga' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.test_phase4_ga' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.test_query_operations' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.test_records_operations' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.test_tables_operations' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.core' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.core.test_auth' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.core.test_http_client' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.core.test_http_errors' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.core.test_http_logger' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.data' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.data.test_batch_edge_cases' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.data.test_batch_serialization' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.data.test_enum_optionset_payload' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.data.test_format_key' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.data.test_list_columns' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.data.test_logical_crud' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.data.test_odata_internal' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.data.test_relationships' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.data.test_sql_guardrails' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.data.test_sql_parse' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.data.test_upload' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.models' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.models.test_alternate_key' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.models.test_filters' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.models.test_labels' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.models.test_query_builder' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.models.test_record' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.models.test_relationship' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.models.test_relationship_info' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nWARNING: autodoc: failed to import module 'unit.models.test_table_info' from module 'tests'; the following exception was raised:\r\nNo module named 'tests.unit'\r\nC:\\Users\\abelmilash\\Repos\\PowerPlatform-DataverseClient-Python\\.conda\\Lib\\site-packages\\py2docfx\\source_repo\\0\\doc\\examples.rst: WARNING: document isn't included in any toctree\r\nC:\\Users\\abelmilash\\Repos\\PowerPlatform-DataverseClient-Python\\.conda\\Lib\\site-packages\\py2docfx\\source_repo\\0\\doc\\src.rst: WARNING: document isn't included in any toctree\r\nC:\\Users\\abelmilash\\Repos\\PowerPlatform-DataverseClient-Python\\.conda\\Lib\\site-packages\\py2docfx\\source_repo\\0\\doc\\tests.rst: WARNING: document isn't included in any toctree\r\n" diff --git a/scratch_async_demo.py b/scratch_async_demo.py deleted file mode 100644 index c97b5b3c..00000000 --- a/scratch_async_demo.py +++ /dev/null @@ -1,82 +0,0 @@ -""" -Demo: async-only SDK usage patterns and asyncio.run() behavior. - -Run with: - PYTHONPATH=src python scratch_async_demo.py -""" - -import asyncio - - -# --------------------------------------------------------------------------- -# Pretend this is an "async-only SDK" — no sync client exists -# --------------------------------------------------------------------------- - -async def sdk_create_record(table: str, data: dict) -> str: - """Simulated async-only SDK method.""" - await asyncio.sleep(0) # yields control, like a real network call - return f"fake-guid-for-{data.get('name', 'unknown')}" - - -# --------------------------------------------------------------------------- -# Part 1: How sync users must call an async-only SDK -# --------------------------------------------------------------------------- - -print("=" * 60) -print("PART 0: Calling async function directly (no await, no asyncio.run)") -print("=" * 60) - -result = sdk_create_record("account", {"name": "Contoso"}) -print(f"Return value: {result}") -print(f"Type: {type(result)}") -print("The coroutine was never executed — no network call happened.") -print() - -print("=" * 60) -print("PART 1: Sync usage of an async-only function") -print("=" * 60) - -# Sync user wraps every call in asyncio.run() -record_id = asyncio.run(sdk_create_record("account", {"name": "Contoso"})) -print(f"Created record: {record_id}") -print() -print("Sync call required:") -print(" asyncio.run(client.records.create('account', {'name': 'Contoso'}))") -print("Instead of the simpler sync SDK syntax:") -print(" client.records.create('account', {'name': 'Contoso'})") -print() - - -# --------------------------------------------------------------------------- -# Part 2: asyncio.run() raises RuntimeError when a loop is already running -# (simulates Jupyter notebook / FastAPI endpoint / pytest-asyncio) -# --------------------------------------------------------------------------- - -print("=" * 60) -print("PART 2: asyncio.run() inside a running event loop") -print(" (simulates Jupyter / FastAPI / pytest-asyncio)") -print("=" * 60) - -async def simulate_jupyter_cell(): - """ - Jupyter runs all cells inside a single persistent event loop. - Any code in a cell is already inside that running loop. - A sync user who tries asyncio.run() here hits RuntimeError. - """ - print("Inside running event loop (simulating a Jupyter cell)...") - - # This is what a sync user would try: - try: - result = asyncio.run(sdk_create_record("account", {"name": "Fabrikam"})) - print(f" [unexpected success] {result}") - except RuntimeError as e: - print(f" RuntimeError raised: {e}") - - print() - print(" The only way to call the SDK here is with await:") - result = await sdk_create_record("account", {"name": "Fabrikam"}) - print(f" await result: {result}") - print() - print(" So even 'sync' users in Jupyter must learn async/await.") - -asyncio.run(simulate_jupyter_cell()) diff --git a/sql_examples_output.txt b/sql_examples_output.txt deleted file mode 100644 index bff882d6..00000000 --- a/sql_examples_output.txt +++ /dev/null @@ -1,7 +0,0 @@ -================================================================================ -Dataverse SDK -- SQL End-to-End (Pure SQL Workflows) -================================================================================ - -================================================================================ -1. Setup & Authentication -================================================================================ diff --git a/src/PowerPlatform/Dataverse/aio/data/_async_odata.py b/src/PowerPlatform/Dataverse/aio/data/_async_odata.py index 33b29a48..fa3ea0dc 100644 --- a/src/PowerPlatform/Dataverse/aio/data/_async_odata.py +++ b/src/PowerPlatform/Dataverse/aio/data/_async_odata.py @@ -845,22 +845,18 @@ async def _create_entity( attributes: List[Dict[str, Any]], solution_unique_name: Optional[str] = None, ) -> Dict[str, Any]: - url = f"{self.api}/CreateEntities" + url = f"{self.api}/EntityDefinitions" payload = { - "Entities": [ - { - "@odata.type": "Microsoft.Dynamics.CRM.ComplexEntityMetadata", - "SchemaName": table_schema_name, - "DisplayName": self._label(display_name), - "DisplayCollectionName": self._label(display_name + "s"), - "Description": self._label(f"Custom entity for {display_name}"), - "OwnershipType": "UserOwned", - "HasActivities": False, - "HasNotes": True, - "IsActivity": False, - "Attributes": attributes, - } - ] + "@odata.type": "Microsoft.Dynamics.CRM.EntityMetadata", + "SchemaName": table_schema_name, + "DisplayName": self._label(display_name), + "DisplayCollectionName": self._label(display_name + "s"), + "Description": self._label(f"Custom entity for {display_name}"), + "OwnershipType": "UserOwned", + "HasActivities": False, + "HasNotes": True, + "IsActivity": False, + "Attributes": attributes, } params = None if solution_unique_name: @@ -1343,9 +1339,9 @@ async def _create_table( ) attributes: List[Dict[str, Any]] = [] - attributes.append(self._attribute_payload(primary_attr_schema, "string", is_primary_name=True, complex=True)) + attributes.append(self._attribute_payload(primary_attr_schema, "string", is_primary_name=True)) for col_name, dtype in schema.items(): - payload = self._attribute_payload(col_name, dtype, complex=True) + payload = self._attribute_payload(col_name, dtype) if not payload: raise ValueError(f"Unsupported column type '{dtype}' for '{col_name}'.") attributes.append(payload) From 046489053b6a26accb03351b79a214034718fbae Mon Sep 17 00:00:00 2001 From: Abel Milash Date: Sun, 24 May 2026 14:52:06 -0700 Subject: [PATCH 32/34] Mirror PR #183 (CreateEntities API) in async client Sync side was updated to use the new CreateEntities API in PR #183. The async client has its own copy of _create_entity and _create_table, so we need to mirror those changes manually: - _AsyncODataClient._create_entity(): URL EntityDefinitions -> CreateEntities, payload wrapped in Entities[0] array with @odata.type ComplexEntityMetadata - _AsyncODataClient._create_table(): pass complex=True to _attribute_payload calls so attribute metadata uses the Complex*Metadata variants required by CreateEntities The shared base (_odata_base.py) changes from PR #183 -- including the _attribute_payload(complex=...) parameter and Complex*Metadata output -- flow into the async client automatically via inheritance. This is a clean re-apply of the legitimate diff after reverting commit c1f1237, which accidentally committed many untracked scratch/build files alongside this change. Co-Authored-By: Claude Opus 4.7 --- .../Dataverse/aio/data/_async_odata.py | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/PowerPlatform/Dataverse/aio/data/_async_odata.py b/src/PowerPlatform/Dataverse/aio/data/_async_odata.py index fa3ea0dc..33b29a48 100644 --- a/src/PowerPlatform/Dataverse/aio/data/_async_odata.py +++ b/src/PowerPlatform/Dataverse/aio/data/_async_odata.py @@ -845,18 +845,22 @@ async def _create_entity( attributes: List[Dict[str, Any]], solution_unique_name: Optional[str] = None, ) -> Dict[str, Any]: - url = f"{self.api}/EntityDefinitions" + url = f"{self.api}/CreateEntities" payload = { - "@odata.type": "Microsoft.Dynamics.CRM.EntityMetadata", - "SchemaName": table_schema_name, - "DisplayName": self._label(display_name), - "DisplayCollectionName": self._label(display_name + "s"), - "Description": self._label(f"Custom entity for {display_name}"), - "OwnershipType": "UserOwned", - "HasActivities": False, - "HasNotes": True, - "IsActivity": False, - "Attributes": attributes, + "Entities": [ + { + "@odata.type": "Microsoft.Dynamics.CRM.ComplexEntityMetadata", + "SchemaName": table_schema_name, + "DisplayName": self._label(display_name), + "DisplayCollectionName": self._label(display_name + "s"), + "Description": self._label(f"Custom entity for {display_name}"), + "OwnershipType": "UserOwned", + "HasActivities": False, + "HasNotes": True, + "IsActivity": False, + "Attributes": attributes, + } + ] } params = None if solution_unique_name: @@ -1339,9 +1343,9 @@ async def _create_table( ) attributes: List[Dict[str, Any]] = [] - attributes.append(self._attribute_payload(primary_attr_schema, "string", is_primary_name=True)) + attributes.append(self._attribute_payload(primary_attr_schema, "string", is_primary_name=True, complex=True)) for col_name, dtype in schema.items(): - payload = self._attribute_payload(col_name, dtype) + payload = self._attribute_payload(col_name, dtype, complex=True) if not payload: raise ValueError(f"Unsupported column type '{dtype}' for '{col_name}'.") attributes.append(payload) From 24f66a0b0c9695cb61588e1ffdc33d7f88d8b9a4 Mon Sep 17 00:00:00 2001 From: Abel Milash Date: Sun, 24 May 2026 15:03:35 -0700 Subject: [PATCH 33/34] Add async tests for CreateEntities API mirror (PR #183) The sync side added test_url_targets_create_entities and updated several _create_table assertions to use Entities[0] in PR #183. Those tests only cover the sync _create_entity; the async client has its own copy. Add three tests to cover the async-side behavior: - test_create_entity_url_targets_create_entities: URL ends with /CreateEntities - test_create_entity_payload_wraps_in_entities_array: body has Entities[0] with @odata.type=ComplexEntityMetadata - test_create_table_posts_complex_attribute_metadata: _create_table forwards complex=True so the posted Attributes use Complex*AttributeMetadata variants Tests: 2190 pass (up from 2187), _async_odata.py coverage at 98%. Co-Authored-By: Claude Opus 4.7 --- .../aio/data/test_async_odata_internal.py | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/tests/unit/aio/data/test_async_odata_internal.py b/tests/unit/aio/data/test_async_odata_internal.py index a064c1ed..d5c2fa55 100644 --- a/tests/unit/aio/data/test_async_odata_internal.py +++ b/tests/unit/aio/data/test_async_odata_internal.py @@ -728,6 +728,26 @@ async def test_invalid_display_name_raises(self): with pytest.raises(TypeError): await client._create_table("new_Product", {}, display_name=123) + async def test_create_table_posts_complex_attribute_metadata(self): + """_create_table forwards complex=True so the POST body uses Complex*AttributeMetadata variants.""" + client = _make_client() + not_found = _resp(json_data={"value": []}, status=200) + create_resp = _resp(status=204) + entity_resp = _resp( + json_data=_entity_def(entity_set="new_products", schema="new_Product", logical="new_product"), + status=200, + ) + client._request.side_effect = [not_found, create_resp, entity_resp] + await client._create_table("new_Product", {"new_Title": "string"}) + # second call is the CreateEntities POST + post_call = client._request.call_args_list[1] + body = post_call.kwargs["json"] + attrs = body["Entities"][0]["Attributes"] + types = [a["@odata.type"] for a in attrs] + assert "Microsoft.Dynamics.CRM.ComplexStringAttributeMetadata" in types + # primary-name attribute is also string, so both attrs should be Complex variants + assert all(t.startswith("Microsoft.Dynamics.CRM.Complex") for t in types) + # --------------------------------------------------------------------------- # _create_columns() / _delete_columns() @@ -1717,6 +1737,35 @@ async def test_create_entity_missing_metadata_id_raises(self): with pytest.raises(RuntimeError, match="MetadataId missing"): await client._create_entity("t", "t", "T", []) + async def test_create_entity_url_targets_create_entities(self): + """_create_entity POSTs to /CreateEntities (not /EntityDefinitions).""" + client = _make_client() + client._request.return_value = _resp(status=204) + client._get_entity_by_table_schema_name = AsyncMock( + return_value={"EntitySetName": "ts", "MetadataId": "m1", "LogicalName": "t"} + ) + await client._create_entity("new_table", "New Table", []) + args, _ = client._request.call_args + url = args[1] + assert url.endswith("/CreateEntities") + + async def test_create_entity_payload_wraps_in_entities_array(self): + """_create_entity payload wraps the entity body in an Entities[0] array with ComplexEntityMetadata.""" + client = _make_client() + client._request.return_value = _resp(status=204) + client._get_entity_by_table_schema_name = AsyncMock( + return_value={"EntitySetName": "ts", "MetadataId": "m1", "LogicalName": "t"} + ) + await client._create_entity("new_table", "New Table", [{"SchemaName": "new_col"}]) + _, kwargs = client._request.call_args + body = kwargs["json"] + assert "Entities" in body + assert isinstance(body["Entities"], list) and len(body["Entities"]) == 1 + entity = body["Entities"][0] + assert entity["@odata.type"] == "Microsoft.Dynamics.CRM.ComplexEntityMetadata" + assert entity["SchemaName"] == "new_table" + assert entity["Attributes"] == [{"SchemaName": "new_col"}] + class TestWaitForAttributeVisibilityWithDelay: """Coverage for _wait_for_attribute_visibility() sleep branch.""" From d0b49429bd18a70beb36f634d711a3cf5324f19b Mon Sep 17 00:00:00 2001 From: Abel Milash Date: Sun, 24 May 2026 15:21:39 -0700 Subject: [PATCH 34/34] Mirror picklist example test to async functional_testing PR #183 added a test_picklist_table scenario to the sync functional testing script demonstrating local OptionSet creation via an Enum subclass, label-cache resolution on write, and FormattedValue annotation on read. Mirror the same flow to examples/aio/basic/functional_testing.py so async users have parity coverage: - New async test_picklist_table() using await client.tables.create(), client.records.create(), client.records.retrieve(), client.records.list() - cleanup_test_data() takes an optional picklist_table_schema_name and prompts for the picklist table teardown - Wire test_picklist_table into main() with corresponding summary line Unit tests: 2190 pass (unchanged; this is an example-only script). Co-Authored-By: Claude Opus 4.7 --- examples/aio/basic/functional_testing.py | 142 ++++++++++++++++++++++- 1 file changed, 141 insertions(+), 1 deletion(-) diff --git a/examples/aio/basic/functional_testing.py b/examples/aio/basic/functional_testing.py index 4f078b76..a8180735 100644 --- a/examples/aio/basic/functional_testing.py +++ b/examples/aio/basic/functional_testing.py @@ -25,6 +25,7 @@ import asyncio import sys +from enum import Enum from typing import Optional, Dict, Any from datetime import datetime @@ -1037,10 +1038,123 @@ async def _get_or_create(schema, columns, label): print(f" [WARN] Could not delete {tbl}: {e}") +async def test_picklist_table(client: AsyncDataverseClient) -> str: + """Create a table with a local picklist column and write/read records. + + Demonstrates: + - Defining a local OptionSet via an ``Enum`` subclass passed as the column ``dtype``. + - Optional multi-language labels via the ``__labels__`` class attribute. + - Writing records using either the enum member's integer value OR its label. + - Reading the integer value back, and the formatted label via + ``include_annotations="OData.Community.Display.V1.FormattedValue"``. + + Returns the schema name of the table so the caller can clean it up later. + """ + print("\n-> Picklist Column Test") + print("=" * 50) + + table_schema_name = "test_PicklistAttribute" + + # Define a local option set as an Enum. Optional __labels__ provides + # display labels per language code (1033 = English, 1036 = French). + class TaskStatus(Enum): + NotStarted = 1 + InProgress = 2 + Completed = 3 + Cancelled = 4 + + __labels__ = { + 1033: { + "NotStarted": "Not Started", + "InProgress": "In Progress", + "Completed": "Completed", + "Cancelled": "Cancelled", + }, + 1036: { + "NotStarted": "Non commencé", + "InProgress": "En cours", + "Completed": "Terminé", + "Cancelled": "Annulé", + }, + } + + record_id: Optional[str] = None + try: + # Drop any leftover table from a prior failed run so this example is idempotent. + try: + existing = await client.tables.get(table_schema_name) + if existing: + print(f" Removing leftover '{table_schema_name}' from a previous run...") + await client.tables.delete(table_schema_name) + except Exception: + pass + + print(f"Creating table '{table_schema_name}' with a picklist column 'test_status'...") + + await client.tables.create( + table_schema_name, + primary_column="test_name", + columns={ + "test_status": TaskStatus, # Enum subclass => local picklist + "test_notes": "string", + }, + ) + + table_info = await wait_for_table_metadata(client, table_schema_name) + print(f"[OK] Picklist table ready: entity_set='{table_info.get('entity_set_name')}'") + + # --- Insert one record using the enum's integer value --- + rec_by_int = { + "test_name": f"Picklist Int {datetime.now().strftime('%H:%M:%S')}", + "test_status": TaskStatus.InProgress.value, # integer 2 + "test_notes": "Created using TaskStatus.InProgress.value", + } + record_id = await client.records.create(table_schema_name, rec_by_int) + print(f"[OK] Created record by int value: {record_id} (status={TaskStatus.InProgress.value})") + + # --- Insert another record using the picklist label (SDK resolves label -> int) --- + rec_by_label = { + "test_name": f"Picklist Label {datetime.now().strftime('%H:%M:%S')}", + "test_status": "Completed", # resolved via label cache to int 3 + "test_notes": "Created using label string 'Completed'", + } + record_id_2 = await client.records.create(table_schema_name, rec_by_label) + print(f"[OK] Created record by label: {record_id_2} (status='Completed' -> 3)") + + # --- Read back including the FormattedValue annotation --- + annotation = "OData.Community.Display.V1.FormattedValue" + retrieved = await client.records.retrieve( + table_schema_name, + record_id, + select=["test_name", "test_status"], + include_annotations=annotation, + ) + status_int = retrieved.get("test_status") + status_label = retrieved.get(f"test_status@{annotation}") + print(f" Retrieved: test_status={status_int}, formatted='{status_label}'") + assert status_int == TaskStatus.InProgress.value, f"expected {TaskStatus.InProgress.value}, got {status_int}" + + # --- List records, filtering by the picklist column --- + completed = await client.records.list( + table_schema_name, + select=["test_name", "test_status"], + filter=f"test_status eq {TaskStatus.Completed.value}", + include_annotations=annotation, + ) + print(f"[OK] Query by picklist value found {len(completed)} 'Completed' record(s).") + + except HttpError as e: + print(f"[ERR] HTTP error during picklist test: {e}") + raise + + return table_schema_name + + async def cleanup_test_data( client: AsyncDataverseClient, table_info: Dict[str, Any], record_id: str, + picklist_table_schema_name: Optional[str] = None, ) -> None: """Clean up test data.""" print("\n-> Cleanup") @@ -1089,6 +1203,30 @@ async def cleanup_test_data( else: print("Test table kept for future testing") + # --- Picklist test table cleanup --- + if picklist_table_schema_name: + picklist_cleanup = ( + input(f"Do you want to delete the picklist test table '{picklist_table_schema_name}'? (y/N): ") + .strip() + .lower() + ) + if picklist_cleanup in ["y", "yes"]: + for attempt in range(1, retries + 1): + try: + await client.tables.delete(picklist_table_schema_name) + print(f"[OK] Picklist test table '{picklist_table_schema_name}' deleted successfully") + break + except HttpError as err: + if attempt < retries: + await asyncio.sleep(delay_seconds) + continue + print(f"[WARN] Failed to delete picklist test table: {err}") + except Exception as e: + print(f"[WARN] Failed to delete picklist test table: {e}") + break + else: + print("Picklist test table kept for future testing") + async def main(): """Main async test function.""" @@ -1115,6 +1253,7 @@ async def main(): record_id = await test_create_record(client, table_info) retrieved_record = await test_read_record(client, table_info, record_id) await test_query_records(client, table_info) + picklist_table_info = await test_picklist_table(client) await test_relationships(client) await test_sql_encoding(client, table_info, retrieved_record) await test_batch_all_operations(client, table_info) @@ -1126,12 +1265,13 @@ async def main(): print("[OK] Record Creation: Success") print("[OK] Record Reading: Success") print("[OK] Record Querying (list, list_pages, builder, fetchxml): Success") + print("[OK] Picklist Column: Success") print("[OK] Relationship Operations: Success") print("[OK] SQL Encoding: Success") print("[OK] Batch Operations: Success") print("\nYour async PowerPlatform Dataverse Client SDK is fully functional!") - await cleanup_test_data(client, table_info, record_id) + await cleanup_test_data(client, table_info, record_id, picklist_table_info) finally: await credential.close()

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ⅈHZV3m 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ⅈHZV3m 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